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 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 : IrBuilder> 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<CallableMemberDescriptor>): List<DeclarationDescriptor> {
@@ -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])
}
@@ -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<VariableDescriptor>()
val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>()
@@ -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
}
}
+11
View File
@@ -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"
}
@@ -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