psi2ir: Implicit casts with 'dynamic'
This commit is contained in:
+21
-13
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user