Kapt+JVM_IR: generate delegated members correctly

Generate a declaration for each delegated member without body. If we
don't generate delegated declarations, subclasses will have incorrect IR
with unbound symbols in fake overrides.

 #KT-58027 Fixed
This commit is contained in:
Alexander Udalov
2023-04-17 22:37:29 +02:00
committed by Space Team
parent 5407ac1c72
commit bc7aea1426
7 changed files with 125 additions and 14 deletions
@@ -151,7 +151,11 @@ class DelegationResolver<T : CallableMemberDescriptor> private constructor(
// descriptor = Foo // descriptor = Foo
// toInterface = Bar // toInterface = Bar
// delegateExpressionType = typeof(baz) // delegateExpressionType = typeof(baz)
// return Map<member of Foo, corresponding member of typeOf(baz)> //
// This method returns a map where keys are members of Foo, and values are members of typeof(baz).
//
// In case delegation is to an error type, which is useful for KAPT stub generation mode, typeof(baz) has no members, so we return
// a map from each element to it (so keys = values in the returned map).
fun getDelegates( fun getDelegates(
descriptor: ClassDescriptor, descriptor: ClassDescriptor,
toInterface: ClassDescriptor, toInterface: ClassDescriptor,
@@ -175,8 +179,11 @@ class DelegationResolver<T : CallableMemberDescriptor> private constructor(
val actualDelegates = DescriptorUtils.getAllOverriddenDescriptors(delegatingMember) val actualDelegates = DescriptorUtils.getAllOverriddenDescriptors(delegatingMember)
.filter { it.containingDeclaration == toInterface } .filter { it.containingDeclaration == toInterface }
.map { overriddenDescriptor -> .map { overriddenDescriptor ->
if (scopeType.isError) {
overriddenDescriptor
} else {
val name = overriddenDescriptor.name val name = overriddenDescriptor.name
// this is the actual member of delegateExpressionType that we are delegating to // This is the actual member of delegateExpressionType that we are delegating to.
(scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_OVERRIDES) + (scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_OVERRIDES) +
scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_OVERRIDES)) scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_OVERRIDES))
.firstOrNull { .firstOrNull {
@@ -188,6 +195,7 @@ class DelegationResolver<T : CallableMemberDescriptor> private constructor(
) )
} }
} }
}
actualDelegates.firstOrNull() actualDelegates.firstOrNull()
} }
@@ -324,8 +324,10 @@ internal class ClassGenerator(
// TODO could possibly refer to scoped type parameters for property accessors // TODO could possibly refer to scoped type parameters for property accessors
irFunction.returnType = delegatedDescriptor.returnType!!.toIrType() irFunction.returnType = delegatedDescriptor.returnType!!.toIrType()
if (context.configuration.generateBodies) {
irFunction.body = generateDelegateFunctionBody(irDelegate, delegatedDescriptor, delegateToDescriptor, irFunction) irFunction.body = generateDelegateFunctionBody(irDelegate, delegatedDescriptor, delegateToDescriptor, irFunction)
} }
}
private fun generateDelegateFunctionBody( private fun generateDelegateFunctionBody(
irDelegate: IrField, irDelegate: IrField,
@@ -0,0 +1,39 @@
@kotlin.Metadata()
@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE"})
public abstract interface A {
@org.jetbrains.annotations.NotNull()
public static final A.Companion Companion = null;
public abstract void inject(@org.jetbrains.annotations.NotNull()
A.B b);
@org.jetbrains.annotations.NotNull()
public abstract java.lang.String getX();
@kotlin.Metadata()
public static abstract class B implements A {
public B() {
super();
}
@java.lang.Override()
@org.jetbrains.annotations.NotNull()
public java.lang.String getX() {
return null;
}
@java.lang.Override()
public void inject(@org.jetbrains.annotations.NotNull()
A.B b) {
}
}
@kotlin.Metadata()
public static final class Companion extends A.B {
private Companion() {
super();
}
}
}
@@ -0,0 +1,11 @@
// CORRECT_ERROR_TYPES
@Suppress("UNRESOLVED_REFERENCE")
interface A {
fun inject(b: B)
val x: String
companion object : B()
abstract class B : A by Unresolved
}
@@ -0,0 +1,39 @@
@kotlin.Metadata()
@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE"})
public abstract interface A {
@org.jetbrains.annotations.NotNull()
public static final A.Companion Companion = null;
public abstract void inject(@org.jetbrains.annotations.NotNull()
A.B b);
@org.jetbrains.annotations.NotNull()
public abstract java.lang.String getX();
@kotlin.Metadata()
public static abstract class B implements A {
@java.lang.Override()
public void inject(@org.jetbrains.annotations.NotNull()
A.B b) {
}
@java.lang.Override()
@org.jetbrains.annotations.NotNull()
public java.lang.String getX() {
return null;
}
public B() {
super();
}
}
@kotlin.Metadata()
public static final class Companion extends A.B {
private Companion() {
super();
}
}
}
@@ -157,6 +157,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
runTest("plugins/kapt3/kapt3-compiler/testData/converter/delegatedProperties.kt"); runTest("plugins/kapt3/kapt3-compiler/testData/converter/delegatedProperties.kt");
} }
@Test
@TestMetadata("delegationAndCompanionObject.kt")
public void testDelegationAndCompanionObject() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/delegationAndCompanionObject.kt");
}
@Test @Test
@TestMetadata("deprecated.kt") @TestMetadata("deprecated.kt")
public void testDeprecated() throws Exception { public void testDeprecated() throws Exception {
@@ -157,6 +157,12 @@ public class IrClassFileToSourceStubConverterTestGenerated extends AbstractIrCla
runTest("plugins/kapt3/kapt3-compiler/testData/converter/delegatedProperties.kt"); runTest("plugins/kapt3/kapt3-compiler/testData/converter/delegatedProperties.kt");
} }
@Test
@TestMetadata("delegationAndCompanionObject.kt")
public void testDelegationAndCompanionObject() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/delegationAndCompanionObject.kt");
}
@Test @Test
@TestMetadata("deprecated.kt") @TestMetadata("deprecated.kt")
public void testDeprecated() throws Exception { public void testDeprecated() throws Exception {