Fix type safe barrier bridges for generics with non-trivial upper bound

This commit is contained in:
SvyatoslavScherbina
2020-11-02 20:52:45 +03:00
committed by Stanislav Erokhin
parent 8c5bb6420a
commit 77fcc0d010
5 changed files with 77 additions and 21 deletions
@@ -1459,7 +1459,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
} else {
// E.g. when generating type operation with reified type parameter in the original body of inline function.
kTrue
// TODO: this code should be unreachable, however [BridgesBuilding] generates IR with such type checks.
// TODO: this code should be unreachable, recheck.
}
functionGenerationContext.br(bbExit)
val bbInstanceOfResult = functionGenerationContext.currentBlock
@@ -207,7 +207,12 @@ private fun IrBlockBodyBuilder.buildTypeSafeBarrier(function: IrFunction,
for (i in valueParameters.indices) {
if (!typeSafeBarrierDescription.checkParameter(i))
continue
val type = originalValueParameters[i].type
val type = originalValueParameters[i].type.erasureForTypeOperation()
// Note: erasing to single type is not entirely correct if type parameter has multiple upper bounds.
// In this case the compiler could generate multiple type checks, one for each upper bound.
// But let's keep it simple here for now; JVM backend doesn't do this anyway.
if (!type.isNullableAny()) {
+returnIfBadType(irGet(valueParameters[i]), type,
if (typeSafeBarrierDescription == SpecialGenericSignatures.TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT)
@@ -25,30 +25,33 @@ import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
internal fun IrType.erasureForTypeOperation(): 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.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)
@@ -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"
}
@@ -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<E : Element> : Set<E> {
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<E>): Boolean = TODO()
override fun iterator(): Iterator<E> = TODO()
}
fun set(): Set<Any> = MySet<Element>()
@Test
fun testMySet() {
val set = set()
assertFalse(set.contains(Any()))
assertFalse(set.contains(NotContainedElement))
assertTrue(set.contains(ContainedElement))
}