psi2ir: Implicit casts with 'dynamic'

This commit is contained in:
Dmitry Petrov
2019-02-06 16:54:35 +03:00
parent 3c8f52b436
commit fc76d0970b
6 changed files with 102 additions and 13 deletions
@@ -37,13 +37,12 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
import org.jetbrains.kotlin.psi2ir.containsNull
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.isNullabilityFlexible
import org.jetbrains.kotlin.types.typeUtil.isNullableAny
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlin.types.upperIfFlexible
fun insertImplicitCasts(element: IrElement, context: GeneratorContext) {
element.transformChildren(InsertImplicitCasts(context.builtIns, context.irBuiltIns, context.typeTranslator), null)
@@ -94,10 +93,10 @@ open class InsertImplicitCasts(
statements.forEachIndexed { i, irStatement ->
if (irStatement is IrExpression) {
statements[i] =
if (i == lastIndex)
irStatement.cast(type)
else
irStatement.coerceToUnit()
if (i == lastIndex)
irStatement.cast(type)
else
irStatement.coerceToUnit()
}
}
}
@@ -219,13 +218,17 @@ open class InsertImplicitCasts(
val valueType = this.type.originalKotlinType!!
return when {
KotlinBuiltIns.isUnit(expectedType) ->
expectedType.isUnit() ->
coerceToUnit()
valueType.isNullabilityFlexible() && valueType.containsNull() && !expectedType.containsNull() -> {
val nonNullValueType = valueType.upperIfFlexible().makeNotNullable()
implicitCast(nonNullValueType, IrTypeOperator.IMPLICIT_NOTNULL).cast(expectedType)
}
valueType.isDynamic() && !expectedType.isDynamic() ->
if (expectedType.isNullableAny())
this
else
implicitCast(expectedType, IrTypeOperator.IMPLICIT_CAST)
valueType.isNullabilityFlexible() && valueType.containsNull() && !expectedType.containsNull() ->
implicitNonNull(valueType, expectedType)
KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType.makeNullable()) ->
this
@@ -243,6 +246,11 @@ open class InsertImplicitCasts(
}
}
private fun IrExpression.implicitNonNull(valueType: KotlinType, expectedType: KotlinType): IrExpression {
val nonNullValueType = valueType.upperIfFlexible().makeNotNullable()
return implicitCast(nonNullValueType, IrTypeOperator.IMPLICIT_NOTNULL).cast(expectedType)
}
private fun IrExpression.implicitCast(
targetType: KotlinType,
typeOperator: IrTypeOperator
@@ -0,0 +1,7 @@
// WITH_RUNTIME
fun test1(d: dynamic) = d.toString()
fun test2(d: dynamic) = d.hashCode()
fun test3(d: dynamic) = d.equals(42)
@@ -0,0 +1,26 @@
FILE fqName:<root> fileName:/dynamicAndMembersOfAny.kt
FUN name:test1 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.String flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='test1(dynamic): String'
CALL 'toString(): String' type=kotlin.String origin=null
$this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: superTypes:[]
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
FUN name:test2 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Int flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='test2(dynamic): Int'
CALL 'hashCode(): Int' type=kotlin.Int origin=null
$this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: superTypes:[]
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
FUN name:test3 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Boolean flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='test3(dynamic): Boolean'
CALL 'equals(Any?): Boolean' type=kotlin.Boolean origin=null
$this: TYPE_OP type=kotlin.Any origin=IMPLICIT_CAST typeOperand=kotlin.Any
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Any modality:OPEN visibility:public flags: superTypes:[]
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
other: CONST Int type=kotlin.Int value=42
@@ -0,0 +1,5 @@
// WITH_RUNTIME
fun test1(d: dynamic) = if (d is String) d.length else -1
fun test2(d: dynamic) = if (d is Array<*>) d.size else -1
@@ -0,0 +1,33 @@
FILE fqName:<root> fileName:/dynamicWithImplicitCast.kt
FUN name:test1 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Int flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='test1(dynamic): Int'
WHEN type=kotlin.Int origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.String
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:String modality:FINAL visibility:public flags: superTypes:[kotlin.Comparable<kotlin.String>; kotlin.CharSequence]
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
then: CALL '<get-length>(): Int' type=kotlin.Int origin=GET_PROPERTY
$this: TYPE_OP type=kotlin.String origin=IMPLICIT_CAST typeOperand=kotlin.String
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:String modality:FINAL visibility:public flags: superTypes:[kotlin.Comparable<kotlin.String>; kotlin.CharSequence]
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Int type=kotlin.Int value=-1
FUN name:test2 visibility:public modality:FINAL <> (d:dynamic) returnType:kotlin.Int flags:
VALUE_PARAMETER name:d index:0 type:dynamic flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='test2(dynamic): Int'
WHEN type=kotlin.Int origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Array<*>
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Array modality:FINAL visibility:public flags: superTypes:[kotlin.Any]
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
then: CALL '<get-size>(): Int' type=kotlin.Int origin=GET_PROPERTY
$this: TYPE_OP type=kotlin.Array<out kotlin.Any?> origin=IMPLICIT_CAST typeOperand=kotlin.Array<out kotlin.Any?>
typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Array modality:FINAL visibility:public flags: superTypes:[kotlin.Any]
GET_VAR 'value-parameter d: dynamic' type=dynamic origin=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Int type=kotlin.Int value=-1
@@ -41,6 +41,11 @@ public class IrJsTextTestCaseGenerated extends AbstractIrJsTextTestCase {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irJsText/dynamic"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("dynamicAndMembersOfAny.kt")
public void testDynamicAndMembersOfAny() throws Exception {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicAndMembersOfAny.kt");
}
@TestMetadata("dynamicCall.kt")
public void testDynamicCall() throws Exception {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicCall.kt");
@@ -51,6 +56,11 @@ public class IrJsTextTestCaseGenerated extends AbstractIrJsTextTestCase {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicMemberAccess.kt");
}
@TestMetadata("dynamicWithImplicitCast.kt")
public void testDynamicWithImplicitCast() throws Exception {
runTest("compiler/testData/ir/irJsText/dynamic/dynamicWithImplicitCast.kt");
}
@TestMetadata("invokeOperator.kt")
public void testInvokeOperator() throws Exception {
runTest("compiler/testData/ir/irJsText/dynamic/invokeOperator.kt");