From af86c521014a56b5a30efc3f372abe0b4801aea6 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 30 Sep 2020 13:14:12 +0300 Subject: [PATCH] JVM_IR merge annotations when substituting types Otherwise special annotations such as @EnhancedNullability are lost, which affects JVM signatures. KT-42330 --- .../jetbrains/kotlin/ir/util/IrTypeUtils.kt | 13 ++++-- .../org/jetbrains/kotlin/ir/types/irTypes.kt | 15 +++++++ .../collectionByDelegation3WithFullJdk_ir.txt | 14 +++---- .../collectionStubs/mapOfPrimitivesFullJdk.kt | 3 ++ .../mapOfPrimitivesFullJdk.txt | 40 ++++++++++++++++++ .../mapOfPrimitivesFullJdk_ir.txt | 42 +++++++++++++++++++ .../codegen/BytecodeListingTestGenerated.java | 5 +++ .../ir/IrBytecodeListingTestGenerated.java | 5 +++ 8 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.kt create mode 100644 compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.txt create mode 100644 compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk_ir.txt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt index e3e82d5f367..7a2a75d9ebc 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt @@ -82,8 +82,16 @@ fun IrType.substitute(params: List, arguments: List): I fun IrType.substitute(substitutionMap: Map): IrType { if (this !is IrSimpleType) return this - substitutionMap[classifier]?.let { - return it.withHasQuestionMark(hasQuestionMark || it is IrSimpleType && it.hasQuestionMark) + val newAnnotations = annotations.map { it.deepCopyWithSymbols() } + + substitutionMap[classifier]?.let { substitutedType -> + // Add nullability and annotations from original type + return substitutedType + .withHasQuestionMark( + hasQuestionMark || + substitutedType is IrSimpleType && substitutedType.hasQuestionMark + ) + .addAnnotations(newAnnotations) } val newArguments = arguments.map { @@ -94,7 +102,6 @@ fun IrType.substitute(substitutionMap: Map): IrTy } } - val newAnnotations = annotations.map { it.deepCopyWithSymbols() } return IrSimpleTypeImpl( classifier, hasQuestionMark, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt index 12aadbf567c..c9a4c157a3f 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrTypeParameter +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol @@ -35,6 +36,20 @@ fun IrType.withHasQuestionMark(newHasQuestionMark: Boolean): IrType = else -> this } +fun IrType.addAnnotations(newAnnotations: List): IrType = + if (newAnnotations.isEmpty()) + this + else when (this) { + is IrSimpleType -> + toBuilder().apply { + annotations = annotations + newAnnotations + }.buildSimpleType() + is IrDynamicType -> + IrDynamicTypeImpl(null, annotations + newAnnotations, Variance.INVARIANT) + else -> + this + } + val IrType.classifierOrFail: IrClassifierSymbol get() = cast().classifier diff --git a/compiler/testData/codegen/bytecodeListing/collectionStubs/collectionByDelegation3WithFullJdk_ir.txt b/compiler/testData/codegen/bytecodeListing/collectionStubs/collectionByDelegation3WithFullJdk_ir.txt index b22f1a485a7..543659c413e 100644 --- a/compiler/testData/codegen/bytecodeListing/collectionStubs/collectionByDelegation3WithFullJdk_ir.txt +++ b/compiler/testData/codegen/bytecodeListing/collectionStubs/collectionByDelegation3WithFullJdk_ir.txt @@ -156,11 +156,11 @@ public final class DIntMap { private synthetic final field $$delegate_0: java.util.Map public method (@org.jetbrains.annotations.NotNull p0: java.util.Map): void public method clear(): void - public method compute(p0: int, p1: java.util.function.BiFunction): java.lang.Double + public method compute(p0: java.lang.Integer, p1: java.util.function.BiFunction): java.lang.Double public synthetic bridge method compute(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object - public method computeIfAbsent(p0: int, p1: java.util.function.Function): java.lang.Double + public method computeIfAbsent(p0: java.lang.Integer, p1: java.util.function.Function): java.lang.Double public synthetic bridge method computeIfAbsent(p0: java.lang.Object, p1: java.util.function.Function): java.lang.Object - public method computeIfPresent(p0: int, p1: java.util.function.BiFunction): java.lang.Double + public method computeIfPresent(p0: java.lang.Integer, p1: java.util.function.BiFunction): java.lang.Double public synthetic bridge method computeIfPresent(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object public method containsKey(p0: int): boolean public bridge final method containsKey(p0: java.lang.Object): boolean @@ -176,18 +176,18 @@ public final class DIntMap { public @org.jetbrains.annotations.NotNull method getValues(): java.util.Collection public method isEmpty(): boolean public bridge final method keySet(): java.util.Set - public method merge(p0: int, p1: double, p2: java.util.function.BiFunction): java.lang.Double + public method merge(p0: java.lang.Integer, p1: java.lang.Double, p2: java.util.function.BiFunction): java.lang.Double public synthetic bridge method merge(p0: java.lang.Object, p1: java.lang.Object, p2: java.util.function.BiFunction): java.lang.Object public method put(p0: int, p1: double): java.lang.Double public synthetic bridge method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object public method putAll(p0: java.util.Map): void - public method putIfAbsent(p0: int, p1: double): java.lang.Double + public method putIfAbsent(p0: java.lang.Integer, p1: java.lang.Double): java.lang.Double public synthetic bridge method putIfAbsent(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object public method remove(p0: java.lang.Object): java.lang.Double public synthetic bridge method remove(p0: java.lang.Object): java.lang.Object public method remove(p0: java.lang.Object, p1: java.lang.Object): boolean - public method replace(p0: int, p1: double): java.lang.Double - public method replace(p0: int, p1: double, p2: double): boolean + public method replace(p0: java.lang.Integer, p1: java.lang.Double): java.lang.Double + public method replace(p0: java.lang.Integer, p1: java.lang.Double, p2: java.lang.Double): boolean public synthetic bridge method replace(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object public synthetic bridge method replace(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): boolean public method replaceAll(p0: java.util.function.BiFunction): void diff --git a/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.kt b/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.kt new file mode 100644 index 00000000000..ce3bff008c4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.kt @@ -0,0 +1,3 @@ +// FULL_JDK + +abstract class IntIntMap : Map \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.txt b/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.txt new file mode 100644 index 00000000000..1dc21417771 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.txt @@ -0,0 +1,40 @@ +@kotlin.Metadata +public abstract class IntIntMap { + // source: 'mapOfPrimitivesFullJdk.kt' + public method (): void + public method clear(): void + public method compute(p0: java.lang.Integer, p1: java.util.function.BiFunction): java.lang.Integer + public synthetic method compute(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object + public method computeIfAbsent(p0: java.lang.Integer, p1: java.util.function.Function): java.lang.Integer + public synthetic method computeIfAbsent(p0: java.lang.Object, p1: java.util.function.Function): java.lang.Object + public method computeIfPresent(p0: java.lang.Integer, p1: java.util.function.BiFunction): java.lang.Integer + public synthetic method computeIfPresent(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object + public abstract method containsKey(p0: int): boolean + public bridge final method containsKey(p0: java.lang.Object): boolean + public abstract method containsValue(p0: int): boolean + public bridge final method containsValue(p0: java.lang.Object): boolean + public bridge final method entrySet(): java.util.Set + public abstract method get(p0: int): java.lang.Integer + public bridge final method get(p0: java.lang.Object): java.lang.Object + public abstract method getEntries(): java.util.Set + public abstract method getKeys(): java.util.Set + public abstract method getSize(): int + public abstract method getValues(): java.util.Collection + public bridge final method keySet(): java.util.Set + public method merge(p0: java.lang.Integer, p1: java.lang.Integer, p2: java.util.function.BiFunction): java.lang.Integer + public synthetic method merge(p0: java.lang.Object, p1: java.lang.Object, p2: java.util.function.BiFunction): java.lang.Object + public method put(p0: int, p1: int): java.lang.Integer + public synthetic method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object + public method putAll(p0: java.util.Map): void + public method putIfAbsent(p0: java.lang.Integer, p1: java.lang.Integer): java.lang.Integer + public synthetic method putIfAbsent(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object + public method remove(p0: java.lang.Object): java.lang.Object + public method remove(p0: java.lang.Object, p1: java.lang.Object): boolean + public method replace(p0: java.lang.Integer, p1: java.lang.Integer): java.lang.Integer + public method replace(p0: java.lang.Integer, p1: java.lang.Integer, p2: java.lang.Integer): boolean + public synthetic method replace(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object + public synthetic method replace(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): boolean + public method replaceAll(p0: java.util.function.BiFunction): void + public bridge final method size(): int + public bridge final method values(): java.util.Collection +} diff --git a/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk_ir.txt b/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk_ir.txt new file mode 100644 index 00000000000..00e440066d5 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk_ir.txt @@ -0,0 +1,42 @@ +@kotlin.Metadata +public abstract class IntIntMap { + // source: 'mapOfPrimitivesFullJdk.kt' + public method (): void + public method clear(): void + public method compute(p0: java.lang.Integer, p1: java.util.function.BiFunction): java.lang.Integer + public synthetic bridge method compute(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object + public method computeIfAbsent(p0: java.lang.Integer, p1: java.util.function.Function): java.lang.Integer + public synthetic bridge method computeIfAbsent(p0: java.lang.Object, p1: java.util.function.Function): java.lang.Object + public method computeIfPresent(p0: java.lang.Integer, p1: java.util.function.BiFunction): java.lang.Integer + public synthetic bridge method computeIfPresent(p0: java.lang.Object, p1: java.util.function.BiFunction): java.lang.Object + public abstract method containsKey(p0: int): boolean + public bridge final method containsKey(p0: java.lang.Object): boolean + public abstract method containsValue(p0: int): boolean + public bridge final method containsValue(p0: java.lang.Object): boolean + public bridge final method entrySet(): java.util.Set + public abstract method get(p0: int): java.lang.Integer + public bridge final method get(p0: java.lang.Object): java.lang.Integer + public synthetic bridge method get(p0: java.lang.Object): java.lang.Object + public abstract method getEntries(): java.util.Set + public abstract method getKeys(): java.util.Set + public abstract method getSize(): int + public abstract method getValues(): java.util.Collection + public bridge final method keySet(): java.util.Set + public method merge(p0: java.lang.Integer, p1: java.lang.Integer, p2: java.util.function.BiFunction): java.lang.Integer + public synthetic bridge method merge(p0: java.lang.Object, p1: java.lang.Object, p2: java.util.function.BiFunction): java.lang.Object + public method put(p0: int, p1: int): java.lang.Integer + public synthetic bridge method put(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object + public method putAll(p0: java.util.Map): void + public method putIfAbsent(p0: java.lang.Integer, p1: java.lang.Integer): java.lang.Integer + public synthetic bridge method putIfAbsent(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object + public method remove(p0: java.lang.Object): java.lang.Integer + public synthetic bridge method remove(p0: java.lang.Object): java.lang.Object + public method remove(p0: java.lang.Object, p1: java.lang.Object): boolean + public method replace(p0: java.lang.Integer, p1: java.lang.Integer): java.lang.Integer + public method replace(p0: java.lang.Integer, p1: java.lang.Integer, p2: java.lang.Integer): boolean + public synthetic bridge method replace(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object + public synthetic bridge method replace(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object): boolean + public method replaceAll(p0: java.util.function.BiFunction): void + public bridge final method size(): int + public bridge final method values(): java.util.Collection +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 3bc07ccaac1..c0091628a8e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -284,6 +284,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/extendingAbstractCollection.kt"); } + @TestMetadata("mapOfPrimitivesFullJdk.kt") + public void testMapOfPrimitivesFullJdk() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.kt"); + } + @TestMetadata("noStubsForCollection.kt") public void testNoStubsForCollection() throws Exception { runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/noStubsForCollection.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java index b4d2b3ccd65..14fd0d44c58 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java @@ -284,6 +284,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/extendingAbstractCollection.kt"); } + @TestMetadata("mapOfPrimitivesFullJdk.kt") + public void testMapOfPrimitivesFullJdk() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/mapOfPrimitivesFullJdk.kt"); + } + @TestMetadata("noStubsForCollection.kt") public void testNoStubsForCollection() throws Exception { runTest("compiler/testData/codegen/bytecodeListing/collectionStubs/noStubsForCollection.kt");