From 65444ec6909d31e60fc9501b24d5d674398ad830 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 20 Jan 2017 11:20:32 +0700 Subject: [PATCH] backend: improve type operators support: * add initial support for casts with generics; * optimize out the casts to subtypes Also add tests unchecked_cast{1,2}, the latter is disabled. --- .../kotlin/backend/common/lower/LowerUtils.kt | 28 ++++++------- .../konan/lower/CallableReferenceLowering.kt | 6 +-- .../lower/DefaultParameterStubGenerator.kt | 15 +++---- .../konan/lower/TypeOperatorLowering.kt | 41 ++++++++++++++++--- backend.native/tests/build.gradle | 11 +++++ .../tests/codegen/basics/unchecked_cast1.kt | 16 ++++++++ .../tests/codegen/basics/unchecked_cast2.kt | 10 +++++ 7 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 backend.native/tests/codegen/basics/unchecked_cast1.kt create mode 100644 backend.native/tests/codegen/basics/unchecked_cast2.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt index 3451ad801eb..44c64428ec5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt @@ -18,28 +18,28 @@ import org.jetbrains.kotlin.utils.Printer class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns) -class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : IrGeneratorWithScope { - override val context = IrLoweringContext(backendContext) - override val scope = Scope(functionDescriptor) -} +class FunctionIrBuilder(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : + IrBuilderWithScope( + IrLoweringContext(backendContext), + Scope(functionDescriptor), + UNDEFINED_OFFSET, + UNDEFINED_OFFSET + ) -fun BackendContext.createFunctionIrGenerator(functionDescriptor: FunctionDescriptor) = - FunctionIrGenerator(this, functionDescriptor) +fun BackendContext.createFunctionIrBuilder(functionDescriptor: FunctionDescriptor) = + FunctionIrBuilder(this, functionDescriptor) -class FunctionIrBuilder(context: IrLoweringContext, scope: Scope, startOffset: Int, endOffset: Int) : - IrBuilderWithScope(context, scope, startOffset, endOffset) - -fun FunctionIrGenerator.createIrBuilder() = FunctionIrBuilder(context, scope, UNDEFINED_OFFSET, UNDEFINED_OFFSET) +fun T.at(element: IrElement) = this.at(element.startOffset, element.endOffset) /** * Builds [IrBlock] to be used instead of given expression. */ -inline fun FunctionIrGenerator.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null, - resultType: KotlinType? = expression.type, - body: IrBlockBuilder.() -> Unit) = +inline fun IrGeneratorWithScope.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null, + resultType: KotlinType? = expression.type, + body: IrBlockBuilder.() -> Unit) = this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body) -inline fun FunctionIrGenerator.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) = +inline fun IrGeneratorWithScope.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) = this.irBlockBody(irElement.startOffset, irElement.endOffset, body) fun computeOverrides(current: ClassDescriptor, functionsFromCurrent: List): List { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt index f110aeb70f1..2a4d7b34382 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt @@ -1,7 +1,7 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass -import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator +import org.jetbrains.kotlin.backend.common.lower.createFunctionIrBuilder import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.* @@ -136,8 +136,8 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering, // Create new function descriptor: val newDescriptor = createSimpleFunctionImplTargetDescriptor(descriptor, unboundParams) - val generator = lower.context.createFunctionIrGenerator(newDescriptor) - val blockBody = generator.irBlockBody(startOffset, endOffset) { + val builder = lower.context.createFunctionIrBuilder(newDescriptor) + val blockBody = builder.irBlockBody(startOffset, endOffset) { val boundArgsGet = irCall(simpleFunctionImplBoundArgsGetter).apply { dispatchReceiver = irGet(newDescriptor.valueParameters[0]) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt index ae8a6f3fc75..0943c6f0bde 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt @@ -2,8 +2,8 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass -import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator -import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.common.lower.createFunctionIrBuilder +import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.KonanPlatform import org.jetbrains.kotlin.builtins.KotlinBuiltIns @@ -61,14 +61,9 @@ class DefaultParameterStubGenerator internal constructor(val context: Context): val functionDescriptor = irFunction.descriptor if (bodies.isNotEmpty()) { - val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor() - val generator = context.createFunctionIrGenerator(descriptor) - val builder = generator.createIrBuilder() - builder.apply { - startOffset = irFunction.startOffset - endOffset = irFunction.endOffset - } - val body = generator.irBlockBody { + val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor() + val builder = context.createFunctionIrBuilder(descriptor) + val body = builder.irBlockBody(irFunction) { val params = mutableListOf() val variables = mutableMapOf() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt index 3c6b574f764..84dbbe15e9d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt @@ -1,10 +1,10 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.FunctionLoweringPass -import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator -import org.jetbrains.kotlin.backend.common.lower.irBlock +import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrFunction @@ -13,6 +13,8 @@ import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf /** * This lowering pass lowers some [IrTypeOperatorCall]s. @@ -27,19 +29,45 @@ internal class TypeOperatorLowering(val context: Context) : FunctionLoweringPass private class TypeOperatorTransformer(val context: Context, val function: FunctionDescriptor) : IrElementTransformerVoid() { - private val generator = context.createFunctionIrGenerator(function) + private val builder = context.createFunctionIrBuilder(function) override fun visitFunction(declaration: IrFunction): IrStatement { // ignore inner functions during this pass return declaration } + private tailrec fun KotlinType.erasure(): KotlinType { + val descriptor = this.constructor.declarationDescriptor + return if (descriptor is TypeParameterDescriptor) { + val upperBound = descriptor.upperBounds.singleOrNull() ?: + TODO("$descriptor : ${descriptor.upperBounds}") + + upperBound.erasure() + } else { + this + } + } + + private fun lowerCast(expression: IrTypeOperatorCall): IrExpression { + val typeOperand = expression.typeOperand.erasure() + return if (expression.argument.type.isSubtypeOf(typeOperand)) { + // TODO: consider the case when expression type is wrong e.g. due to generics-related unchecked casts. + expression.argument + } else if (typeOperand == expression.typeOperand) { + expression + } else { + builder.at(expression).irAs(expression.argument, typeOperand) + } + } + private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression { - return generator.irBlock(expression) { + val typeOperand = expression.typeOperand.erasure() + + return builder.irBlock(expression) { +irLet(expression.argument) { variable -> irIfThenElse(expression.type, - condition = irIs(irGet(variable), expression.typeOperand), - thenPart = irImplicitCast(irGet(variable), expression.typeOperand), + condition = irIs(irGet(variable), typeOperand), + thenPart = irImplicitCast(irGet(variable), typeOperand), elsePart = irNull()) } } @@ -50,6 +78,7 @@ private class TypeOperatorTransformer(val context: Context, val function: Functi return when (expression.operator) { IrTypeOperator.SAFE_CAST -> lowerSafeCast(expression) + IrTypeOperator.CAST -> lowerCast(expression) else -> expression } } diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 1ee692920fa..2862fd98bdd 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -208,6 +208,17 @@ task cast_simple(type: RunKonanTest) { source = "codegen/basics/cast_simple.kt" } +task unchecked_cast1(type: RunKonanTest) { + goldValue = "17\n17\n42\n42\n" + source = "codegen/basics/unchecked_cast1.kt" +} + +task unchecked_cast2(type: RunKonanTest) { + disabled = true + goldValue = "Ok\n" + source = "codegen/basics/unchecked_cast2.kt" +} + task null_check(type: RunKonanTest) { source = "codegen/basics/null_check.kt" } diff --git a/backend.native/tests/codegen/basics/unchecked_cast1.kt b/backend.native/tests/codegen/basics/unchecked_cast1.kt new file mode 100644 index 00000000000..1cb92d6b866 --- /dev/null +++ b/backend.native/tests/codegen/basics/unchecked_cast1.kt @@ -0,0 +1,16 @@ +fun main(args: Array) { + foo("17") + bar("17") + foo(42) + bar(42) +} + +fun foo(x: Any?) { + val y = x as T + println(y.toString()) +} + +fun bar(x: Any?) { + val y = x as? T + println(y.toString()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/basics/unchecked_cast2.kt b/backend.native/tests/codegen/basics/unchecked_cast2.kt new file mode 100644 index 00000000000..ffde9b02e41 --- /dev/null +++ b/backend.native/tests/codegen/basics/unchecked_cast2.kt @@ -0,0 +1,10 @@ +fun main(args: Array) { + try { + val x = cast(Any()) + println(x.length) + } catch (e: Throwable) { + println("Ok") + } +} + +fun cast(x: Any?) = x as T \ No newline at end of file