JVM_IR KT-42260 add abstract overrides for generated stubs

This commit is contained in:
Dmitry Petrov
2020-09-29 12:08:12 +03:00
parent 445b2d6eb1
commit 1fc459ab4c
21 changed files with 323 additions and 19 deletions
@@ -4934,6 +4934,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM_IR, testDataFilePath, "// IGNORE_BACKEND_FIR: ");
}
@TestMetadata("addCollectionStubWithCovariantOverride.kt")
public void testAddCollectionStubWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/box/collections/addCollectionStubWithCovariantOverride.kt");
}
public void testAllFilesPresentInCollections() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/collections"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@@ -58,22 +58,28 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
// We don't need to generate stub for existing methods, but for FAKE_OVERRIDE methods with ABSTRACT modality,
// it means an abstract function in superclass that is not implemented yet,
// stub generation is still needed to avoid invocation error.
val existingMethodsByNameAndArity = irClass.functions
.filterNot { it.modality == Modality.ABSTRACT && it.isFakeOverride }
.groupBy { it.nameAndArity }
val (abstractMethods, nonAbstractMethods) = irClass.functions.partition { it.modality == Modality.ABSTRACT && it.isFakeOverride }
val nonAbstractMethodsByNameAndArity = nonAbstractMethods.groupBy { it.nameAndArity }
val abstractMethodsByNameAndArity = abstractMethods.groupBy { it.nameAndArity }
for (stub in methodStubsToGenerate) {
val relevantMembers = existingMethodsByNameAndArity[stub.nameAndArity].orEmpty()
val existingOverrides = relevantMembers.filter { isStubOverriddenByExistingFun(stub, it) }
val stubNameAndArity = stub.nameAndArity
val relevantMembers = nonAbstractMethodsByNameAndArity[stubNameAndArity].orEmpty()
val existingOverrides = relevantMembers.filter { isOverriddenBy(stub, it) }
if (existingOverrides.isNotEmpty()) {
// In the case that we find a defined method that matches the stub signature,
// we add the overridden symbols to that defined method,
// so that bridge lowering can still generate correct bridge for that method.
existingOverrides.forEach { it.overriddenSymbols += stub.overriddenSymbols }
// We don't add a throwing stub if it's effectively overridden by an existing function.
continue
}
// Generated stub might still override some abstract member(s), which affects resulting method signature.
val overriddenAbstractMethods = abstractMethodsByNameAndArity[stubNameAndArity].orEmpty().filter { isOverriddenBy(it, stub) }
stub.overriddenSymbols += overriddenAbstractMethods.map { it.symbol }
// Some stub members require special handling.
// In both 'remove' and 'removeAt' cases there are no other member functions with same name in built-in mutable collection
// classes, so it's safe to check for the member name itself.
@@ -188,30 +194,29 @@ internal class CollectionStubMethodLowering(val context: JvmBackendContext) : Cl
}
}
private fun isStubOverriddenByExistingFun(stubFun: IrSimpleFunction, existingFun: IrSimpleFunction): Boolean {
// We don't add a throwing stub if it's effectively overridden by an existing function.
// This is true if all of the following conditions are met,
// assuming type parameter Ti of the existing function is "equal" to type parameter Si of the generated stub:
private fun isOverriddenBy(superFun: IrSimpleFunction, overridingFun: IrSimpleFunction): Boolean {
// Function 'f0' is overridden by function 'f1' if all of the following conditions are met,
// assuming type parameter Ti of 'f1' is "equal" to type parameter Si of 'f0':
// - names are same;
// - existing function has the same number of type parameters,
// - 'f1' has the same number of type parameters,
// and upper bounds for type parameters are equivalent;
// - existing function has the same number of value parameters,
// - 'f1' has the same number of value parameters,
// and types for value parameters are equivalent;
// - return type of the existing function is a subtype of return type of the generated stub.
// - 'f1' return type is a subtype of 'f0' return type.
if (stubFun.name != existingFun.name) return false
if (stubFun.typeParameters.size != existingFun.typeParameters.size) return false
if (stubFun.valueParameters.size != existingFun.valueParameters.size) return false
if (superFun.name != overridingFun.name) return false
if (superFun.typeParameters.size != overridingFun.typeParameters.size) return false
if (superFun.valueParameters.size != overridingFun.valueParameters.size) return false
val typeChecker = createTypeChecker(stubFun, existingFun)
val typeChecker = createTypeChecker(superFun, overridingFun)
// Note that type parameters equivalence check doesn't really happen on collection stubs
// (because members of Kotlin built-in collection classes don't have type parameters of their own),
// but we keep it here for the sake of consistency.
if (!areTypeParametersEquivalent(existingFun, stubFun, typeChecker)) return false
if (!areTypeParametersEquivalent(overridingFun, superFun, typeChecker)) return false
if (!areValueParametersEquivalent(existingFun, stubFun, typeChecker)) return false
if (!isReturnTypeOverrideCompliant(existingFun, stubFun, typeChecker)) return false
if (!areValueParametersEquivalent(overridingFun, superFun, typeChecker)) return false
if (!isReturnTypeOverrideCompliant(overridingFun, superFun, typeChecker)) return false
return true
}
@@ -0,0 +1,26 @@
// IGNORE_BACKEND_FIR: JVM_IR
abstract class AbstractAdd {
abstract fun add(s: String): Any
}
abstract class AbstractStringCollection : AbstractAdd(), Collection<String>
class StringCollection : AbstractStringCollection() {
override fun add(s: String) = s
override val size: Int get() = TODO()
override fun contains(element: String): Boolean = TODO()
override fun containsAll(elements: Collection<String>): Boolean = TODO()
override fun isEmpty(): Boolean = TODO()
override fun iterator(): Iterator<String> = TODO()
}
fun test1(a: AbstractAdd) =
a.add("O") as String
fun test2(a: AbstractStringCollection) =
a.add("K") as String
fun box() =
test1(StringCollection()) + test2(StringCollection())
@@ -0,0 +1,13 @@
abstract class AbstractAdd {
abstract fun add(s: String): Boolean
}
class StringCollection : AbstractAdd(), Collection<String> {
override fun add(s: String) = false
override val size: Int get() = TODO()
override fun contains(element: String): Boolean = TODO()
override fun containsAll(elements: Collection<String>): Boolean = TODO()
override fun isEmpty(): Boolean = TODO()
override fun iterator(): Iterator<String> = TODO()
}
@@ -0,0 +1,28 @@
@kotlin.Metadata
public abstract class AbstractAdd {
// source: 'stubForAbstractFun.kt'
public method <init>(): void
public abstract method add(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
}
@kotlin.Metadata
public final class StringCollection {
// source: 'stubForAbstractFun.kt'
public method <init>(): void
public method add(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public synthetic bridge method add(p0: java.lang.Object): boolean
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public method contains(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public bridge final method contains(p0: java.lang.Object): boolean
public method containsAll(@org.jetbrains.annotations.NotNull p0: java.util.Collection): boolean
public method getSize(): int
public method isEmpty(): boolean
public @org.jetbrains.annotations.NotNull method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): boolean
public method removeAll(p0: java.util.Collection): boolean
public method retainAll(p0: java.util.Collection): boolean
public bridge final method size(): int
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@@ -0,0 +1,5 @@
abstract class AbstractAdd {
abstract fun add(s: String): Boolean
}
abstract class AbstractStringCollection : AbstractAdd(), Collection<String>
@@ -0,0 +1,26 @@
@kotlin.Metadata
public abstract class AbstractAdd {
// source: 'stubForAbstractFunInAbstractClass.kt'
public method <init>(): void
public abstract method add(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
}
@kotlin.Metadata
public abstract class AbstractStringCollection {
// source: 'stubForAbstractFunInAbstractClass.kt'
public method <init>(): void
public synthetic method add(p0: java.lang.Object): boolean
public method add(p0: java.lang.String): boolean
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public bridge final method contains(p0: java.lang.Object): boolean
public abstract method contains(p0: java.lang.String): boolean
public abstract method getSize(): int
public method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): boolean
public method removeAll(p0: java.util.Collection): boolean
public method retainAll(p0: java.util.Collection): boolean
public bridge final method size(): int
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@@ -0,0 +1,5 @@
abstract class AbstractAdd {
abstract fun add(s: String): Any
}
abstract class AbstractStringCollection : AbstractAdd(), Collection<String>
@@ -0,0 +1,26 @@
@kotlin.Metadata
public abstract class AbstractAdd {
// source: 'stubForAbstractFunInAbstractClassWithCovariantOverride.kt'
public method <init>(): void
public abstract @org.jetbrains.annotations.NotNull method add(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.Object
}
@kotlin.Metadata
public abstract class AbstractStringCollection {
// source: 'stubForAbstractFunInAbstractClassWithCovariantOverride.kt'
public method <init>(): void
public synthetic method add(p0: java.lang.Object): boolean
public method add(p0: java.lang.String): java.lang.Boolean
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public bridge final method contains(p0: java.lang.Object): boolean
public abstract method contains(p0: java.lang.String): boolean
public abstract method getSize(): int
public method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): boolean
public method removeAll(p0: java.util.Collection): boolean
public method retainAll(p0: java.util.Collection): boolean
public bridge final method size(): int
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@@ -0,0 +1,27 @@
@kotlin.Metadata
public abstract class AbstractAdd {
// source: 'stubForAbstractFunInAbstractClassWithCovariantOverride.kt'
public method <init>(): void
public abstract @org.jetbrains.annotations.NotNull method add(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.Object
}
@kotlin.Metadata
public abstract class AbstractStringCollection {
// source: 'stubForAbstractFunInAbstractClassWithCovariantOverride.kt'
public method <init>(): void
public synthetic bridge method add(p0: java.lang.Object): boolean
public method add(p0: java.lang.String): java.lang.Boolean
public synthetic bridge method add(p0: java.lang.String): java.lang.Object
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public bridge final method contains(p0: java.lang.Object): boolean
public abstract method contains(p0: java.lang.String): boolean
public abstract method getSize(): int
public method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): boolean
public method removeAll(p0: java.util.Collection): boolean
public method retainAll(p0: java.util.Collection): boolean
public bridge final method size(): int
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@@ -0,0 +1,26 @@
@kotlin.Metadata
public abstract class AbstractAdd {
// source: 'stubForAbstractFunInAbstractClass.kt'
public method <init>(): void
public abstract method add(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
}
@kotlin.Metadata
public abstract class AbstractStringCollection {
// source: 'stubForAbstractFunInAbstractClass.kt'
public method <init>(): void
public synthetic bridge method add(p0: java.lang.Object): boolean
public method add(p0: java.lang.String): boolean
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public bridge final method contains(p0: java.lang.Object): boolean
public abstract method contains(p0: java.lang.String): boolean
public abstract method getSize(): int
public method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): boolean
public method removeAll(p0: java.util.Collection): boolean
public method retainAll(p0: java.util.Collection): boolean
public bridge final method size(): int
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@@ -0,0 +1,13 @@
abstract class AbstractAdd {
abstract fun add(s: String): Any
}
class StringCollection : AbstractAdd(), Collection<String> {
override fun add(s: String) = false
override val size: Int get() = TODO()
override fun contains(element: String): Boolean = TODO()
override fun containsAll(elements: Collection<String>): Boolean = TODO()
override fun isEmpty(): Boolean = TODO()
override fun iterator(): Iterator<String> = TODO()
}
@@ -0,0 +1,29 @@
@kotlin.Metadata
public abstract class AbstractAdd {
// source: 'stubForAbstractFunWithCovariantOverride.kt'
public method <init>(): void
public abstract @org.jetbrains.annotations.NotNull method add(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.Object
}
@kotlin.Metadata
public final class StringCollection {
// source: 'stubForAbstractFunWithCovariantOverride.kt'
public method <init>(): void
public @org.jetbrains.annotations.NotNull method add(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.Boolean
public synthetic bridge method add(p0: java.lang.Object): boolean
public synthetic bridge method add(p0: java.lang.String): java.lang.Object
public method addAll(p0: java.util.Collection): boolean
public method clear(): void
public method contains(@org.jetbrains.annotations.NotNull p0: java.lang.String): boolean
public bridge final method contains(p0: java.lang.Object): boolean
public method containsAll(@org.jetbrains.annotations.NotNull p0: java.util.Collection): boolean
public method getSize(): int
public method isEmpty(): boolean
public @org.jetbrains.annotations.NotNull method iterator(): java.util.Iterator
public method remove(p0: java.lang.Object): boolean
public method removeAll(p0: java.util.Collection): boolean
public method retainAll(p0: java.util.Collection): boolean
public bridge final method size(): int
public method toArray(): java.lang.Object[]
public method toArray(p0: java.lang.Object[]): java.lang.Object[]
}
@@ -4964,6 +4964,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
@TestMetadata("addCollectionStubWithCovariantOverride.kt")
public void testAddCollectionStubWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/box/collections/addCollectionStubWithCovariantOverride.kt");
}
public void testAllFilesPresentInCollections() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/collections"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@@ -299,6 +299,26 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/observableMutableMap.kt");
}
@TestMetadata("stubForAbstractFun.kt")
public void testStubForAbstractFun() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubForAbstractFun.kt");
}
@TestMetadata("stubForAbstractFunInAbstractClass.kt")
public void testStubForAbstractFunInAbstractClass() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubForAbstractFunInAbstractClass.kt");
}
@TestMetadata("stubForAbstractFunInAbstractClassWithCovariantOverride.kt")
public void testStubForAbstractFunInAbstractClassWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubForAbstractFunInAbstractClassWithCovariantOverride.kt");
}
@TestMetadata("stubForAbstractFunWithCovariantOverride.kt")
public void testStubForAbstractFunWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubForAbstractFunWithCovariantOverride.kt");
}
@TestMetadata("stubsFromSuperclass.kt")
public void testStubsFromSuperclass() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubsFromSuperclass.kt");
@@ -4964,6 +4964,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
@TestMetadata("addCollectionStubWithCovariantOverride.kt")
public void testAddCollectionStubWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/box/collections/addCollectionStubWithCovariantOverride.kt");
}
public void testAllFilesPresentInCollections() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/collections"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@@ -4934,6 +4934,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
@TestMetadata("addCollectionStubWithCovariantOverride.kt")
public void testAddCollectionStubWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/box/collections/addCollectionStubWithCovariantOverride.kt");
}
public void testAllFilesPresentInCollections() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/collections"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@@ -299,6 +299,26 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/observableMutableMap.kt");
}
@TestMetadata("stubForAbstractFun.kt")
public void testStubForAbstractFun() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubForAbstractFun.kt");
}
@TestMetadata("stubForAbstractFunInAbstractClass.kt")
public void testStubForAbstractFunInAbstractClass() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubForAbstractFunInAbstractClass.kt");
}
@TestMetadata("stubForAbstractFunInAbstractClassWithCovariantOverride.kt")
public void testStubForAbstractFunInAbstractClassWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubForAbstractFunInAbstractClassWithCovariantOverride.kt");
}
@TestMetadata("stubForAbstractFunWithCovariantOverride.kt")
public void testStubForAbstractFunWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubForAbstractFunWithCovariantOverride.kt");
}
@TestMetadata("stubsFromSuperclass.kt")
public void testStubsFromSuperclass() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/stubsFromSuperclass.kt");
@@ -4064,6 +4064,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
}
@TestMetadata("addCollectionStubWithCovariantOverride.kt")
public void testAddCollectionStubWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/box/collections/addCollectionStubWithCovariantOverride.kt");
}
public void testAllFilesPresentInCollections() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/collections"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
@@ -4064,6 +4064,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
@TestMetadata("addCollectionStubWithCovariantOverride.kt")
public void testAddCollectionStubWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/box/collections/addCollectionStubWithCovariantOverride.kt");
}
public void testAllFilesPresentInCollections() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/collections"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
@@ -4064,6 +4064,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
@TestMetadata("addCollectionStubWithCovariantOverride.kt")
public void testAddCollectionStubWithCovariantOverride() throws Exception {
runTest("compiler/testData/codegen/box/collections/addCollectionStubWithCovariantOverride.kt");
}
public void testAllFilesPresentInCollections() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/collections"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}