FIR: Support proper implicit return type computation for local classes
This commit is contained in:
+3
-9
@@ -5,11 +5,8 @@ FILE: localImplicitBodies.kt
|
|||||||
super<R|kotlin/Any|>()
|
super<R|kotlin/Any|>()
|
||||||
}
|
}
|
||||||
|
|
||||||
public final fun sss(): <ERROR TYPE REF: Cannot calculate return type during full-body resolution (local class/object?): public final fun abc(): <implicit> {
|
public final fun sss(): R|kotlin/Int| {
|
||||||
^abc IntegerLiteral(1)
|
^sss this@R|/anonymous|.R|/anonymous.abc|()
|
||||||
}
|
|
||||||
> {
|
|
||||||
^sss R?C|/anonymous.abc|()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final fun abc(): R|kotlin/Int| {
|
public final fun abc(): R|kotlin/Int| {
|
||||||
@@ -18,8 +15,5 @@ FILE: localImplicitBodies.kt
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lval g: <ERROR TYPE REF: Cannot calculate return type during full-body resolution (local class/object?): public final fun abc(): <implicit> {
|
lval g: R|kotlin/Int| = R|<local>/x|.R|/anonymous.sss|()
|
||||||
^abc IntegerLiteral(1)
|
|
||||||
}
|
|
||||||
> = R|<local>/x|.R?C|/anonymous.sss|()
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ FILE: O.kt
|
|||||||
super<R|kotlin/Any|>()
|
super<R|kotlin/Any|>()
|
||||||
}
|
}
|
||||||
|
|
||||||
public final val zz: R|kotlin/Boolean| = R|<local>/z|
|
public final val zz: R|kotlin/Boolean| = this@R|/O.Derived.Some|.R|/O.Derived.Some.z|
|
||||||
public get(): R|kotlin/Boolean|
|
public get(): R|kotlin/Boolean|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
private val x = object {
|
||||||
|
fun foo(x: Int) = /* here we have not computed return type for "bar" */bar(x)
|
||||||
|
fun bar(y: Int) = this.hashCode() + y > 0
|
||||||
|
|
||||||
|
val w get() = z
|
||||||
|
val z get() = this.hashCode() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun useBoolean(b: Boolean) {}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
useBoolean(x.foo(1))
|
||||||
|
useBoolean(x.bar(2))
|
||||||
|
useBoolean(x.w)
|
||||||
|
useBoolean(x.z)
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
FILE: implicitInAnonymous.kt
|
||||||
|
private final val x: R|anonymous| = object : R|kotlin/Any| {
|
||||||
|
private constructor(): R|anonymous| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun foo(x: R|kotlin/Int|): R|kotlin/Boolean| {
|
||||||
|
^foo this@R|/anonymous|.R|/anonymous.bar|(R|<local>/x|)
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun bar(y: R|kotlin/Int|): R|kotlin/Boolean| {
|
||||||
|
^bar CMP(>, this@R|/anonymous|.R|kotlin/Any.hashCode|().R|kotlin/Int.plus|(R|<local>/y|).R|kotlin/Int.compareTo|(Int(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public final val w: R|kotlin/Boolean|
|
||||||
|
public get(): R|kotlin/Boolean| {
|
||||||
|
^ this@R|/anonymous|.R|/anonymous.z|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final val z: R|kotlin/Boolean|
|
||||||
|
public get(): R|kotlin/Boolean| {
|
||||||
|
^ ==(this@R|/anonymous|.R|kotlin/Any.hashCode|(), Int(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private get(): R|anonymous|
|
||||||
|
public final fun useBoolean(b: R|kotlin/Boolean|): R|kotlin/Unit| {
|
||||||
|
}
|
||||||
|
public final fun main(): R|kotlin/Unit| {
|
||||||
|
R|/useBoolean|(R|/x|.R|/anonymous.foo|(Int(1)))
|
||||||
|
R|/useBoolean|(R|/x|.R|/anonymous.bar|(Int(2)))
|
||||||
|
R|/useBoolean|(R|/x|.R|/anonymous.w|)
|
||||||
|
R|/useBoolean|(R|/x|.R|/anonymous.z|)
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
fun useBoolean(b: Boolean) {}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
class A {
|
||||||
|
fun foo(x: Int) = bar(x)
|
||||||
|
fun bar(y: Int) = this.hashCode() + y > 0
|
||||||
|
|
||||||
|
val w get() = z
|
||||||
|
val z get() = this.hashCode() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
val a = A()
|
||||||
|
|
||||||
|
useBoolean(a.foo(1))
|
||||||
|
useBoolean(a.bar(1))
|
||||||
|
useBoolean(a.w)
|
||||||
|
useBoolean(a.z)
|
||||||
|
|
||||||
|
class B {
|
||||||
|
fun foo(x: Int) = inner.w
|
||||||
|
fun bar(y: Int) = this.hashCode() + y > 0
|
||||||
|
|
||||||
|
val inner = Inner()
|
||||||
|
|
||||||
|
inner class Inner {
|
||||||
|
val w get() = z
|
||||||
|
val z get() = bar(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val b = B()
|
||||||
|
|
||||||
|
useBoolean(b.foo(1))
|
||||||
|
useBoolean(b.bar(1))
|
||||||
|
useBoolean(b.inner.w)
|
||||||
|
useBoolean(b.inner.z)
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
FILE: implicitInLocalClasses.kt
|
||||||
|
public final fun useBoolean(b: R|kotlin/Boolean|): R|kotlin/Unit| {
|
||||||
|
}
|
||||||
|
public final fun main(): R|kotlin/Unit| {
|
||||||
|
local final class A : R|kotlin/Any| {
|
||||||
|
public constructor(): R|A| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun foo(x: R|kotlin/Int|): R|kotlin/Boolean| {
|
||||||
|
^foo this@R|/A|.R|/A.bar|(R|<local>/x|)
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun bar(y: R|kotlin/Int|): R|kotlin/Boolean| {
|
||||||
|
^bar CMP(>, this@R|/A|.R|kotlin/Any.hashCode|().R|kotlin/Int.plus|(R|<local>/y|).R|kotlin/Int.compareTo|(Int(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public final val w: R|kotlin/Boolean|
|
||||||
|
public get(): R|kotlin/Boolean| {
|
||||||
|
^ this@R|/A|.R|/A.z|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final val z: R|kotlin/Boolean|
|
||||||
|
public get(): R|kotlin/Boolean| {
|
||||||
|
^ ==(this@R|/A|.R|kotlin/Any.hashCode|(), Int(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
lval a: R|A| = R|/A.A|()
|
||||||
|
R|/useBoolean|(R|<local>/a|.R|/A.foo|(Int(1)))
|
||||||
|
R|/useBoolean|(R|<local>/a|.R|/A.bar|(Int(1)))
|
||||||
|
R|/useBoolean|(R|<local>/a|.R|/A.w|)
|
||||||
|
R|/useBoolean|(R|<local>/a|.R|/A.z|)
|
||||||
|
local final class B : R|kotlin/Any| {
|
||||||
|
public constructor(): R|B| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun foo(x: R|kotlin/Int|): R|kotlin/Boolean| {
|
||||||
|
^foo this@R|/B|.R|/B.inner|.R|/B.Inner.w|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final fun bar(y: R|kotlin/Int|): R|kotlin/Boolean| {
|
||||||
|
^bar CMP(>, this@R|/B|.R|kotlin/Any.hashCode|().R|kotlin/Int.plus|(R|<local>/y|).R|kotlin/Int.compareTo|(Int(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public final val inner: R|B.Inner| = this@R|/B|.R|/B.Inner.Inner|()
|
||||||
|
public get(): R|B.Inner|
|
||||||
|
|
||||||
|
local final inner class Inner : R|kotlin/Any| {
|
||||||
|
public constructor(): R|B.Inner| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final val w: R|kotlin/Boolean|
|
||||||
|
public get(): R|kotlin/Boolean| {
|
||||||
|
^ this@R|/B.Inner|.R|/B.Inner.z|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final val z: R|kotlin/Boolean|
|
||||||
|
public get(): R|kotlin/Boolean| {
|
||||||
|
^ this@R|/B|.R|/B.bar|(Int(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
lval b: R|B| = R|/B.B|()
|
||||||
|
R|/useBoolean|(R|<local>/b|.R|/B.foo|(Int(1)))
|
||||||
|
R|/useBoolean|(R|<local>/b|.R|/B.bar|(Int(1)))
|
||||||
|
R|/useBoolean|(R|<local>/b|.R|/B.inner|.R|/B.Inner.w|)
|
||||||
|
R|/useBoolean|(R|<local>/b|.R|/B.inner|.R|/B.Inner.z|)
|
||||||
|
}
|
||||||
Generated
+23
@@ -1314,6 +1314,29 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/localClasses")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class LocalClasses extends AbstractFirDiagnosticsTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInLocalClasses() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/localClasses"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicitInAnonymous.kt")
|
||||||
|
public void testImplicitInAnonymous() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/localClasses/implicitInAnonymous.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicitInLocalClasses.kt")
|
||||||
|
public void testImplicitInLocalClasses() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/localClasses/implicitInLocalClasses.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/multifile")
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/multifile")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+23
@@ -1314,6 +1314,29 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/localClasses")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class LocalClasses extends AbstractFirDiagnosticsWithLightTreeTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInLocalClasses() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/localClasses"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicitInAnonymous.kt")
|
||||||
|
public void testImplicitInAnonymous() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/localClasses/implicitInAnonymous.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicitInLocalClasses.kt")
|
||||||
|
public void testImplicitInLocalClasses() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/localClasses/implicitInLocalClasses.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/multifile")
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/multifile")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+19
-2
@@ -99,7 +99,10 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
class BodyResolveContext(val returnTypeCalculator: ReturnTypeCalculator) {
|
class BodyResolveContext(
|
||||||
|
val returnTypeCalculator: ReturnTypeCalculator,
|
||||||
|
val targetedLocalClasses: Set<FirClass<*>> = emptySet()
|
||||||
|
) {
|
||||||
val fileImportsScope: MutableList<FirScope> = mutableListOf()
|
val fileImportsScope: MutableList<FirScope> = mutableListOf()
|
||||||
|
|
||||||
@set:PrivateForInline
|
@set:PrivateForInline
|
||||||
@@ -189,13 +192,27 @@ abstract class FirAbstractBodyResolveTransformer(phase: FirResolvePhase) : FirAb
|
|||||||
val lastScope = localScopes.lastOrNull() ?: return
|
val lastScope = localScopes.lastOrNull() ?: return
|
||||||
localScopes = localScopes.set(localScopes.size - 1, lastScope.transform())
|
localScopes = localScopes.set(localScopes.size - 1, lastScope.transform())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
fun createSnapshotForLocalClasses(
|
||||||
|
returnTypeCalculator: ReturnTypeCalculator,
|
||||||
|
targetedLocalClasses: Set<FirClass<*>>
|
||||||
|
) = BodyResolveContext(returnTypeCalculator, targetedLocalClasses).apply {
|
||||||
|
fileImportsScope.addAll(this@BodyResolveContext.fileImportsScope)
|
||||||
|
typeParametersScopes = this@BodyResolveContext.typeParametersScopes
|
||||||
|
localScopes = this@BodyResolveContext.localScopes
|
||||||
|
file = this@BodyResolveContext.file
|
||||||
|
implicitReceiverStack = this@BodyResolveContext.implicitReceiverStack
|
||||||
|
localContextForAnonymousFunctions.putAll(this@BodyResolveContext.localContextForAnonymousFunctions)
|
||||||
|
containerIfAny = this@BodyResolveContext.containerIfAny
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BodyResolveTransformerComponents(
|
class BodyResolveTransformerComponents(
|
||||||
override val session: FirSession,
|
override val session: FirSession,
|
||||||
override val scopeSession: ScopeSession,
|
override val scopeSession: ScopeSession,
|
||||||
val transformer: FirBodyResolveTransformer,
|
val transformer: FirBodyResolveTransformer,
|
||||||
private val context: BodyResolveContext
|
val context: BodyResolveContext
|
||||||
) : BodyResolveComponents {
|
) : BodyResolveComponents {
|
||||||
override val fileImportsScope: List<FirScope> get() = context.fileImportsScope
|
override val fileImportsScope: List<FirScope> get() = context.fileImportsScope
|
||||||
override val typeParametersScopes: List<FirScope> get() = context.typeParametersScopes
|
override val typeParametersScopes: List<FirScope> get() = context.typeParametersScopes
|
||||||
|
|||||||
+22
-5
@@ -309,6 +309,14 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun transformRegularClass(regularClass: FirRegularClass, data: ResolutionMode): CompositeTransformResult<FirStatement> {
|
override fun transformRegularClass(regularClass: FirRegularClass, data: ResolutionMode): CompositeTransformResult<FirStatement> {
|
||||||
|
if (regularClass.symbol.classId.isLocal && regularClass !in context.targetedLocalClasses) {
|
||||||
|
return FirImplicitTypeBodyResolveTransformerAdapterForLocalClasses(
|
||||||
|
components, data
|
||||||
|
).transformRegularClass(regularClass, null).also {
|
||||||
|
context.storeClass(regularClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.storeClass(regularClass)
|
context.storeClass(regularClass)
|
||||||
return withTypeParametersOf(regularClass) {
|
return withTypeParametersOf(regularClass) {
|
||||||
if (regularClass.symbol.classId.isLocal) {
|
if (regularClass.symbol.classId.isLocal) {
|
||||||
@@ -338,7 +346,16 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transformAnonymousObject(anonymousObject: FirAnonymousObject, data: ResolutionMode): CompositeTransformResult<FirStatement> {
|
override fun transformAnonymousObject(
|
||||||
|
anonymousObject: FirAnonymousObject,
|
||||||
|
data: ResolutionMode
|
||||||
|
): CompositeTransformResult<FirStatement> {
|
||||||
|
if (anonymousObject !in context.targetedLocalClasses) {
|
||||||
|
return FirImplicitTypeBodyResolveTransformerAdapterForLocalClasses(
|
||||||
|
components, data
|
||||||
|
).transformAnonymousObject(anonymousObject, null)
|
||||||
|
}
|
||||||
|
|
||||||
prepareLocalClassForBodyResolve(anonymousObject)
|
prepareLocalClassForBodyResolve(anonymousObject)
|
||||||
val type = anonymousObject.defaultType()
|
val type = anonymousObject.defaultType()
|
||||||
anonymousObject.resultType = buildResolvedTypeRef {
|
anonymousObject.resultType = buildResolvedTypeRef {
|
||||||
@@ -640,17 +657,17 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
|
|||||||
): T {
|
): T {
|
||||||
val (implicitReceiverValue, implicitCompanionValues) = components.collectImplicitReceivers(type, owner)
|
val (implicitReceiverValue, implicitCompanionValues) = components.collectImplicitReceivers(type, owner)
|
||||||
implicitCompanionValues.forEach { value ->
|
implicitCompanionValues.forEach { value ->
|
||||||
implicitReceiverStack.add(null, value)
|
context.implicitReceiverStack.add(null, value)
|
||||||
}
|
}
|
||||||
implicitReceiverValue?.let { implicitReceiverStack.add(labelName, it) }
|
implicitReceiverValue?.let { context.implicitReceiverStack.add(labelName, it) }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return block()
|
return block()
|
||||||
} finally {
|
} finally {
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
implicitReceiverStack.pop(labelName)
|
context.implicitReceiverStack.pop(labelName)
|
||||||
for (i in implicitCompanionValues.indices)
|
for (i in implicitCompanionValues.indices)
|
||||||
implicitReceiverStack.pop(null)
|
context.implicitReceiverStack.pop(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+119
-17
@@ -7,12 +7,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
|
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
|
||||||
|
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
|
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
import org.jetbrains.kotlin.fir.render
|
import org.jetbrains.kotlin.fir.render
|
||||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
@@ -25,6 +27,7 @@ import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
|
|||||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
||||||
|
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||||
import org.jetbrains.kotlin.fir.visitors.compose
|
import org.jetbrains.kotlin.fir.visitors.compose
|
||||||
|
|
||||||
@@ -51,6 +54,95 @@ class FirImplicitTypeBodyResolveTransformerAdapter(private val scopeSession: Sco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FirImplicitTypeBodyResolveTransformerAdapterForLocalClasses(
|
||||||
|
private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents,
|
||||||
|
private val resolutionMode: ResolutionMode,
|
||||||
|
) : FirTransformer<Nothing?>() {
|
||||||
|
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
|
||||||
|
return element.compose()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun transformRegularClass(regularClass: FirRegularClass, data: Nothing?): CompositeTransformResult<FirStatement> {
|
||||||
|
return transformClass(regularClass, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun transformAnonymousObject(anonymousObject: FirAnonymousObject, data: Nothing?): CompositeTransformResult<FirStatement> {
|
||||||
|
return transformClass(anonymousObject, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <F : FirClass<F>> transformClass(klass: FirClass<F>, data: Nothing?): CompositeTransformResult<FirStatement> {
|
||||||
|
val (designationMap, targetedClasses) = CollectingVisitor().run {
|
||||||
|
visitClass(klass)
|
||||||
|
resultingMap to targetedClasses
|
||||||
|
}
|
||||||
|
|
||||||
|
val implicitBodyResolveComputationSession =
|
||||||
|
((components.returnTypeCalculator as? ReturnTypeCalculatorWithJump)?.implicitBodyResolveComputationSession
|
||||||
|
?: ImplicitBodyResolveComputationSession())
|
||||||
|
val returnTypeCalculator = ReturnTypeCalculatorWithJump(
|
||||||
|
components.session,
|
||||||
|
components.scopeSession,
|
||||||
|
implicitBodyResolveComputationSession,
|
||||||
|
designationMap,
|
||||||
|
)
|
||||||
|
|
||||||
|
val newContext = components.context.createSnapshotForLocalClasses(returnTypeCalculator, targetedClasses)
|
||||||
|
returnTypeCalculator.outerBodyResolveContext = newContext
|
||||||
|
|
||||||
|
val transformer = FirImplicitAwareBodyResolveTransformer(
|
||||||
|
components.session, components.scopeSession,
|
||||||
|
implicitBodyResolveComputationSession,
|
||||||
|
FirResolvePhase.BODY_RESOLVE,
|
||||||
|
outerBodyResolveContext = newContext,
|
||||||
|
implicitTypeOnly = false,
|
||||||
|
returnTypeCalculator
|
||||||
|
)
|
||||||
|
|
||||||
|
return klass.transform(transformer, resolutionMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CollectingVisitor : FirDefaultVisitorVoid() {
|
||||||
|
val resultingMap = mutableMapOf<FirCallableMemberDeclaration<*>, List<FirClass<*>>>()
|
||||||
|
val targetedClasses = mutableSetOf<FirClass<*>>()
|
||||||
|
private var currentPath = persistentListOf<FirClass<*>>()
|
||||||
|
|
||||||
|
override fun visitElement(element: FirElement) {}
|
||||||
|
|
||||||
|
override fun visitRegularClass(regularClass: FirRegularClass) {
|
||||||
|
visitClass(regularClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitAnonymousObject(anonymousObject: FirAnonymousObject) {
|
||||||
|
visitClass(anonymousObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <F : FirClass<F>> visitClass(klass: FirClass<F>) {
|
||||||
|
targetedClasses.add(klass)
|
||||||
|
val prev = currentPath
|
||||||
|
currentPath = currentPath.add(klass)
|
||||||
|
|
||||||
|
klass.acceptChildren(this)
|
||||||
|
|
||||||
|
currentPath = prev
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) {
|
||||||
|
visitCallableMemberDeclaration(simpleFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitProperty(property: FirProperty) {
|
||||||
|
visitCallableMemberDeclaration(property)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <F : FirCallableMemberDeclaration<F>> visitCallableMemberDeclaration(
|
||||||
|
callableMemberDeclaration: FirCallableMemberDeclaration<F>
|
||||||
|
) {
|
||||||
|
if (callableMemberDeclaration.returnTypeRef !is FirImplicitTypeRef) return
|
||||||
|
resultingMap[callableMemberDeclaration] = currentPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createReturnTypeCalculatorForIDE(session: FirSession, scopeSession: ScopeSession): ReturnTypeCalculator =
|
fun createReturnTypeCalculatorForIDE(session: FirSession, scopeSession: ScopeSession): ReturnTypeCalculator =
|
||||||
ReturnTypeCalculatorWithJump(session, scopeSession, ImplicitBodyResolveComputationSession())
|
ReturnTypeCalculatorWithJump(session, scopeSession, ImplicitBodyResolveComputationSession())
|
||||||
|
|
||||||
@@ -105,7 +197,7 @@ private open class FirImplicitAwareBodyResolveTransformer(
|
|||||||
require(status is ImplicitBodyResolveComputationStatus.NotComputed) {
|
require(status is ImplicitBodyResolveComputationStatus.NotComputed) {
|
||||||
"Unexpected status in transformCallableMember ($status) for ${member.render()}"
|
"Unexpected status in transformCallableMember ($status) for ${member.render()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitBodyResolveComputationSession.startComputing(symbol)
|
implicitBodyResolveComputationSession.startComputing(symbol)
|
||||||
val result = transform()
|
val result = transform()
|
||||||
implicitBodyResolveComputationSession.storeResult(symbol, result.single)
|
implicitBodyResolveComputationSession.storeResult(symbol, result.single)
|
||||||
@@ -117,9 +209,12 @@ private open class FirImplicitAwareBodyResolveTransformer(
|
|||||||
private class ReturnTypeCalculatorWithJump(
|
private class ReturnTypeCalculatorWithJump(
|
||||||
private val session: FirSession,
|
private val session: FirSession,
|
||||||
private val scopeSession: ScopeSession,
|
private val scopeSession: ScopeSession,
|
||||||
private val implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession
|
val implicitBodyResolveComputationSession: ImplicitBodyResolveComputationSession,
|
||||||
|
val designationMapForLocalClasses: Map<FirCallableMemberDeclaration<*>, List<FirClass<*>>> = mapOf()
|
||||||
) : ReturnTypeCalculator {
|
) : ReturnTypeCalculator {
|
||||||
|
|
||||||
|
var outerBodyResolveContext: FirAbstractBodyResolveTransformer.BodyResolveContext? = null
|
||||||
|
|
||||||
override fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef {
|
override fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef {
|
||||||
if (declaration is FirValueParameter && declaration.returnTypeRef is FirImplicitTypeRef) {
|
if (declaration is FirValueParameter && declaration.returnTypeRef is FirImplicitTypeRef) {
|
||||||
// TODO?
|
// TODO?
|
||||||
@@ -150,29 +245,36 @@ private class ReturnTypeCalculatorWithJump(
|
|||||||
|
|
||||||
val provider = session.firProvider
|
val provider = session.firProvider
|
||||||
|
|
||||||
val file = provider.getFirCallableContainerFile(symbol)
|
val (designation, outerBodyResolveContext) = if (declaration in designationMapForLocalClasses) {
|
||||||
|
designationMapForLocalClasses.getValue(declaration) to outerBodyResolveContext
|
||||||
|
} else {
|
||||||
|
val file = provider.getFirCallableContainerFile(symbol)
|
||||||
|
|
||||||
val outerClasses = generateSequence(id.classId) { classId ->
|
val outerClasses = generateSequence(id.classId) { classId ->
|
||||||
classId.outerClassId
|
classId.outerClassId
|
||||||
}.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it) }
|
}.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it) }
|
||||||
|
|
||||||
if (file == null || outerClasses.any { it == null }) {
|
if (file == null || outerClasses.any { it == null }) {
|
||||||
return buildErrorTypeRef {
|
return buildErrorTypeRef {
|
||||||
diagnostic = ConeSimpleDiagnostic(
|
diagnostic = ConeSimpleDiagnostic(
|
||||||
"Cannot calculate return type (local class/object?)",
|
"Cannot calculate return type (local class/object?)",
|
||||||
DiagnosticKind.InferenceError
|
DiagnosticKind.InferenceError
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
(listOf(file) + outerClasses.filterNotNull().asReversed()) to null
|
||||||
}
|
}
|
||||||
val designation = (listOf(file) + outerClasses.filterNotNull().asReversed() + listOf(declaration))
|
|
||||||
val transformer = FirDesignatedBodyResolveTransformerForReturnTypeCalculator(
|
val transformer = FirDesignatedBodyResolveTransformerForReturnTypeCalculator(
|
||||||
designation.iterator(),
|
(designation.drop(1) + declaration).iterator(),
|
||||||
file.session,
|
session,
|
||||||
scopeSession,
|
scopeSession,
|
||||||
implicitBodyResolveComputationSession
|
implicitBodyResolveComputationSession,
|
||||||
|
this,
|
||||||
|
outerBodyResolveContext
|
||||||
)
|
)
|
||||||
|
|
||||||
file.transform<FirElement, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
|
designation.first().transform<FirElement, ResolutionMode>(transformer, ResolutionMode.ContextDependent)
|
||||||
|
|
||||||
val transformedDeclaration = transformer.lastResult as? FirCallableMemberDeclaration<*>
|
val transformedDeclaration = transformer.lastResult as? FirCallableMemberDeclaration<*>
|
||||||
?: error("Unexpected lastResult: ${transformer.lastResult?.render()}")
|
?: error("Unexpected lastResult: ${transformer.lastResult?.render()}")
|
||||||
|
|||||||
Vendored
-2
@@ -1,2 +0,0 @@
|
|||||||
Failures detected in FirImplicitTypeBodyResolveTransformerAdapter, file: /localReturnInsidePropertyAccessor.fir.kt
|
|
||||||
Cause: java.lang.NullPointerException
|
|
||||||
+2
-1
@@ -258,7 +258,8 @@ FILE fqName:<root> fileName:/enum.kt
|
|||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:z type:kotlin.Int visibility:private [final]' type=kotlin.Unit origin=null
|
SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:z type:kotlin.Int visibility:private [final]' type=kotlin.Unit origin=null
|
||||||
receiver: GET_VAR '<this>: <root>.TestEnum4.TEST2 declared in <root>.TestEnum4.TEST2' type=<root>.TestEnum4.TEST2 origin=null
|
receiver: GET_VAR '<this>: <root>.TestEnum4.TEST2 declared in <root>.TestEnum4.TEST2' type=<root>.TestEnum4.TEST2 origin=null
|
||||||
value: GET_VAR 'x: kotlin.Int declared in <root>.TestEnum4.<init>' type=kotlin.Int origin=null
|
value: CALL 'public final fun <get-x> (): kotlin.Int declared in <root>.TestEnum4' type=kotlin.Int origin=GET_PROPERTY
|
||||||
|
$this: GET_VAR '<this>: <root>.TestEnum4.TEST2 declared in <root>.TestEnum4.TEST2' type=<root>.TestEnum4.TEST2 origin=null
|
||||||
FUN name:foo visibility:public modality:FINAL <> ($this:<root>.TestEnum4.TEST2) returnType:kotlin.Unit
|
FUN name:foo visibility:public modality:FINAL <> ($this:<root>.TestEnum4.TEST2) returnType:kotlin.Unit
|
||||||
$this: VALUE_PARAMETER name:<this> type:<root>.TestEnum4.TEST2
|
$this: VALUE_PARAMETER name:<this> type:<root>.TestEnum4.TEST2
|
||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
|
|||||||
@@ -87,7 +87,8 @@ FILE fqName:<root> fileName:/multipleThisReferences.kt
|
|||||||
CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS
|
CALL 'public final fun plus (other: kotlin.Int): kotlin.Int [operator] declared in kotlin.Int' type=kotlin.Int origin=PLUS
|
||||||
$this: CALL 'public final fun <get-x> (): kotlin.Int declared in <root>.Outer.Inner' type=kotlin.Int origin=GET_PROPERTY
|
$this: CALL 'public final fun <get-x> (): kotlin.Int declared in <root>.Outer.Inner' type=kotlin.Int origin=GET_PROPERTY
|
||||||
$this: GET_VAR '<this>: <root>.Host.test.<no name provided> declared in <root>.Host.test.<no name provided>' type=<root>.Host.test.<no name provided> origin=null
|
$this: GET_VAR '<this>: <root>.Host.test.<no name provided> declared in <root>.Host.test.<no name provided>' type=<root>.Host.test.<no name provided> origin=null
|
||||||
other: GET_VAR 'y: kotlin.Int declared in <root>.Host.<init>' type=kotlin.Int origin=null
|
other: CALL 'public final fun <get-y> (): kotlin.Int declared in <root>.Host' type=kotlin.Int origin=GET_PROPERTY
|
||||||
|
$this: GET_VAR '<this>: <root>.Host declared in <root>.Host.test' type=<root>.Host origin=null
|
||||||
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-xx> visibility:public modality:FINAL <> ($this:<root>.Host.test.<no name provided>) returnType:kotlin.Int
|
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-xx> visibility:public modality:FINAL <> ($this:<root>.Host.test.<no name provided>) returnType:kotlin.Int
|
||||||
correspondingProperty: PROPERTY name:xx visibility:public modality:FINAL [val]
|
correspondingProperty: PROPERTY name:xx visibility:public modality:FINAL [val]
|
||||||
$this: VALUE_PARAMETER name:<this> type:<root>.Host.test.<no name provided>
|
$this: VALUE_PARAMETER name:<this> type:<root>.Host.test.<no name provided>
|
||||||
|
|||||||
Reference in New Issue
Block a user