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.
This commit is contained in:
Svyatoslav Scherbina
2017-01-20 11:20:32 +07:00
committed by SvyatoslavScherbina
parent 9bf0380048
commit 65444ec690
7 changed files with 94 additions and 33 deletions
@@ -18,28 +18,28 @@ import org.jetbrains.kotlin.utils.Printer
class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns) class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns)
class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : IrGeneratorWithScope { class FunctionIrBuilder(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) :
override val context = IrLoweringContext(backendContext) IrBuilderWithScope(
override val scope = Scope(functionDescriptor) IrLoweringContext(backendContext),
} Scope(functionDescriptor),
UNDEFINED_OFFSET,
UNDEFINED_OFFSET
)
fun BackendContext.createFunctionIrGenerator(functionDescriptor: FunctionDescriptor) = fun BackendContext.createFunctionIrBuilder(functionDescriptor: FunctionDescriptor) =
FunctionIrGenerator(this, functionDescriptor) FunctionIrBuilder(this, functionDescriptor)
class FunctionIrBuilder(context: IrLoweringContext, scope: Scope, startOffset: Int, endOffset: Int) : fun <T : IrBuilder> T.at(element: IrElement) = this.at(element.startOffset, element.endOffset)
IrBuilderWithScope(context, scope, startOffset, endOffset)
fun FunctionIrGenerator.createIrBuilder() = FunctionIrBuilder(context, scope, UNDEFINED_OFFSET, UNDEFINED_OFFSET)
/** /**
* Builds [IrBlock] to be used instead of given expression. * Builds [IrBlock] to be used instead of given expression.
*/ */
inline fun FunctionIrGenerator.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null, inline fun IrGeneratorWithScope.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null,
resultType: KotlinType? = expression.type, resultType: KotlinType? = expression.type,
body: IrBlockBuilder.() -> Unit) = body: IrBlockBuilder.() -> Unit) =
this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body) 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) this.irBlockBody(irElement.startOffset, irElement.endOffset, body)
fun computeOverrides(current: ClassDescriptor, functionsFromCurrent: List<CallableMemberDescriptor>): List<DeclarationDescriptor> { fun computeOverrides(current: ClassDescriptor, functionsFromCurrent: List<CallableMemberDescriptor>): List<DeclarationDescriptor> {
@@ -1,7 +1,7 @@
package org.jetbrains.kotlin.backend.konan.lower package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass 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.*
import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.ir.*
@@ -136,8 +136,8 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering,
// Create new function descriptor: // Create new function descriptor:
val newDescriptor = createSimpleFunctionImplTargetDescriptor(descriptor, unboundParams) val newDescriptor = createSimpleFunctionImplTargetDescriptor(descriptor, unboundParams)
val generator = lower.context.createFunctionIrGenerator(newDescriptor) val builder = lower.context.createFunctionIrBuilder(newDescriptor)
val blockBody = generator.irBlockBody(startOffset, endOffset) { val blockBody = builder.irBlockBody(startOffset, endOffset) {
val boundArgsGet = irCall(simpleFunctionImplBoundArgsGetter).apply { val boundArgsGet = irCall(simpleFunctionImplBoundArgsGetter).apply {
dispatchReceiver = irGet(newDescriptor.valueParameters[0]) dispatchReceiver = irGet(newDescriptor.valueParameters[0])
} }
@@ -2,8 +2,8 @@ package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass 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.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanPlatform import org.jetbrains.kotlin.backend.konan.KonanPlatform
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -61,14 +61,9 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
val functionDescriptor = irFunction.descriptor val functionDescriptor = irFunction.descriptor
if (bodies.isNotEmpty()) { if (bodies.isNotEmpty()) {
val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor() val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor()
val generator = context.createFunctionIrGenerator(descriptor) val builder = context.createFunctionIrBuilder(descriptor)
val builder = generator.createIrBuilder() val body = builder.irBlockBody(irFunction) {
builder.apply {
startOffset = irFunction.startOffset
endOffset = irFunction.endOffset
}
val body = generator.irBlockBody {
val params = mutableListOf<VariableDescriptor>() val params = mutableListOf<VariableDescriptor>()
val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>() val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>()
@@ -1,10 +1,10 @@
package org.jetbrains.kotlin.backend.konan.lower package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFunction 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.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid 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. * 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 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 { override fun visitFunction(declaration: IrFunction): IrStatement {
// ignore inner functions during this pass // ignore inner functions during this pass
return declaration 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 { private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression {
return generator.irBlock(expression) { val typeOperand = expression.typeOperand.erasure()
return builder.irBlock(expression) {
+irLet(expression.argument) { variable -> +irLet(expression.argument) { variable ->
irIfThenElse(expression.type, irIfThenElse(expression.type,
condition = irIs(irGet(variable), expression.typeOperand), condition = irIs(irGet(variable), typeOperand),
thenPart = irImplicitCast(irGet(variable), expression.typeOperand), thenPart = irImplicitCast(irGet(variable), typeOperand),
elsePart = irNull()) elsePart = irNull())
} }
} }
@@ -50,6 +78,7 @@ private class TypeOperatorTransformer(val context: Context, val function: Functi
return when (expression.operator) { return when (expression.operator) {
IrTypeOperator.SAFE_CAST -> lowerSafeCast(expression) IrTypeOperator.SAFE_CAST -> lowerSafeCast(expression)
IrTypeOperator.CAST -> lowerCast(expression)
else -> expression else -> expression
} }
} }
+11
View File
@@ -208,6 +208,17 @@ task cast_simple(type: RunKonanTest) {
source = "codegen/basics/cast_simple.kt" 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) { task null_check(type: RunKonanTest) {
source = "codegen/basics/null_check.kt" source = "codegen/basics/null_check.kt"
} }
@@ -0,0 +1,16 @@
fun main(args: Array<String>) {
foo<String>("17")
bar<String>("17")
foo<String>(42)
bar<String>(42)
}
fun <T> foo(x: Any?) {
val y = x as T
println(y.toString())
}
fun <T> bar(x: Any?) {
val y = x as? T
println(y.toString())
}
@@ -0,0 +1,10 @@
fun main(args: Array<String>) {
try {
val x = cast<String>(Any())
println(x.length)
} catch (e: Throwable) {
println("Ok")
}
}
fun <T> cast(x: Any?) = x as T