Fix type safe barrier bridges for generics with non-trivial upper bound
This commit is contained in:
committed by
Stanislav Erokhin
parent
8c5bb6420a
commit
77fcc0d010
+1
-1
@@ -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
|
||||
|
||||
+6
-1
@@ -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)
|
||||
|
||||
+22
-19
@@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user