From 77fcc0d010444ae2ec18bb98b1e18e028367092c Mon Sep 17 00:00:00 2001 From: SvyatoslavScherbina Date: Mon, 2 Nov 2020 20:52:45 +0300 Subject: [PATCH] Fix type safe barrier bridges for generics with non-trivial upper bound --- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 2 +- .../backend/konan/lower/BridgesBuilding.kt | 7 ++- .../konan/lower/TypeOperatorLowering.kt | 41 +++++++++-------- .../backend.native/tests/build.gradle | 4 ++ .../tests/codegen/bridges/specialGeneric.kt | 44 +++++++++++++++++++ 5 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 kotlin-native/backend.native/tests/codegen/bridges/specialGeneric.kt diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 3f102561879..0832299a239 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1459,7 +1459,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map this + is IrTypeParameterSymbol -> { + val upperBound = classifier.owner.superTypes.firstOrNull() + ?: TODO("${classifier.descriptor} : ${classifier.descriptor.upperBounds}") + + if (this.hasQuestionMark) { + // `T?` + upperBound.erasureForTypeOperation().makeNullable() + } else { + upperBound.erasureForTypeOperation() + } + } + else -> TODO(classifier.toString()) + } +} + internal class TypeOperatorLowering(val context: CommonBackendContext) : FileLoweringPass, IrBuildingTransformer(context) { + override fun lower(irFile: IrFile) { irFile.transformChildren(this, null) } - private fun IrType.erasure(): IrType { - if (this !is IrSimpleType) return this - - return when (val classifier = classifier) { - is IrClassSymbol -> this - is IrTypeParameterSymbol -> { - val upperBound = classifier.owner.superTypes.firstOrNull() ?: - TODO("${classifier.descriptor} : ${classifier.descriptor.upperBounds}") - - if (this.hasQuestionMark) { - // `T?` - upperBound.erasure().makeNullable() - } else { - upperBound.erasure() - } - } - else -> TODO(classifier.toString()) - } - } + private fun IrType.erasure(): IrType = this.erasureForTypeOperation() private fun lowerCast(expression: IrTypeOperatorCall): IrExpression { builder.at(expression) diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 072a924b0ea..2557e1a3a88 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -1540,6 +1540,10 @@ task bridges_special(type: KonanLocalTest) { source = "codegen/bridges/special.kt" } +task bridges_specialGeneric(type: KonanLocalTest) { + source = "codegen/bridges/specialGeneric.kt" +} + task bridges_nativePointed(type: KonanLocalTest) { source = "codegen/bridges/nativePointed.kt" } diff --git a/kotlin-native/backend.native/tests/codegen/bridges/specialGeneric.kt b/kotlin-native/backend.native/tests/codegen/bridges/specialGeneric.kt new file mode 100644 index 00000000000..5749dc36dba --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/bridges/specialGeneric.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package codegen.bridges.specialGeneric + +import kotlin.test.* + +interface Element { + val isContained: Boolean +} + +object ContainedElement : Element { + override val isContained: Boolean = true +} + +object NotContainedElement : Element { + override val isContained: Boolean = false +} + +internal class MySet : Set { + override fun contains(element: E): Boolean = element.isContained + + override fun equals(other: Any?): Boolean = TODO() + override fun hashCode(): Int = TODO() + override fun toString(): String = TODO() + + override val size: Int get() = TODO() + override fun isEmpty(): Boolean = TODO() + override fun containsAll(elements: Collection): Boolean = TODO() + + override fun iterator(): Iterator = TODO() +} + +fun set(): Set = MySet() + +@Test +fun testMySet() { + val set = set() + assertFalse(set.contains(Any())) + assertFalse(set.contains(NotContainedElement)) + assertTrue(set.contains(ContainedElement)) +}