From 9638af042d4138b889193d478e4bd3015e0cfd62 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Fri, 4 Jun 2021 13:22:51 +0300 Subject: [PATCH] Support proper behavior in interpreter for cast of null value --- .../kotlin/ir/interpreter/IrInterpreter.kt | 28 ++++++++++++------- .../intrinsics/IntrinsicImplementations.kt | 1 + .../exceptions/nullPointerException.kt | 11 ++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index 02dd0f0d94d..1f30e8b0014 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -456,33 +456,41 @@ class IrInterpreter private constructor( val isErased = typeClassifier.owner is IrTypeParameter && !isReified val typeOperand = if (isReified) (callStack.getState(typeClassifier) as KTypeState).irType else expression.typeOperand + val state = callStack.popState() when (expression.operator) { IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> { - callStack.popState() + // do nothing // callStack.pushState(getUnitState()) TODO find real use cases for this } IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> { - if (!isErased && !callStack.peekState()!!.isSubtypeOf(typeOperand)) { - val convertibleClassName = callStack.popState().irClass.fqNameWhenAvailable - ClassCastException("$convertibleClassName cannot be cast to ${typeOperand.render()}").handleUserException(environment) + when { + state.isNull() && !typeOperand.isNullable() -> NullPointerException().handleUserException(environment) + !isErased && !state.isSubtypeOf(typeOperand) -> { + val castedClassName = state.irClass.fqNameWhenAvailable + ClassCastException("$castedClassName cannot be cast to ${typeOperand.render()}").handleUserException(environment) + } + else -> callStack.pushState(state) } } IrTypeOperator.SAFE_CAST -> { - if (!isErased && !callStack.peekState()!!.isSubtypeOf(typeOperand)) { - callStack.popState() - callStack.pushState(null.toState(irBuiltIns.nothingNType)) + when { + !isErased && !state.isSubtypeOf(typeOperand) -> callStack.pushState(null.toState(irBuiltIns.nothingNType)) + else -> callStack.pushState(state) } } IrTypeOperator.INSTANCEOF -> { - val isInstance = callStack.popState().isSubtypeOf(typeOperand) || isErased + val isInstance = state.isSubtypeOf(typeOperand) || isErased callStack.pushState(isInstance.toState(irBuiltIns.booleanType)) } IrTypeOperator.NOT_INSTANCEOF -> { - val isInstance = callStack.popState().isSubtypeOf(typeOperand) || isErased + val isInstance = state.isSubtypeOf(typeOperand) || isErased callStack.pushState((!isInstance).toState(irBuiltIns.booleanType)) } IrTypeOperator.IMPLICIT_NOTNULL -> { - + when { + state.isNull() && !typeOperand.isNullable() -> NullPointerException().handleUserException(environment) + else -> callStack.pushState(state) + } } else -> TODO("${expression.operator} not implemented") } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt index 8aa1eea2198..bec0d775528 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt @@ -290,6 +290,7 @@ internal object AssertIntrinsic : IntrinsicBase() { val messageLambda = environment.callStack.getState(irFunction.valueParameters.last().symbol) as KFunctionState val function = messageLambda.irFunction as IrSimpleFunction + environment.callStack.loadUpValues(messageLambda) val call = IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.returnType, function.symbol) return listOf(customEvaluateInstruction(irFunction, environment), CompoundInstruction(call)) diff --git a/compiler/testData/ir/interpreter/exceptions/nullPointerException.kt b/compiler/testData/ir/interpreter/exceptions/nullPointerException.kt index cc5471ae1bb..0885d5f85ab 100644 --- a/compiler/testData/ir/interpreter/exceptions/nullPointerException.kt +++ b/compiler/testData/ir/interpreter/exceptions/nullPointerException.kt @@ -38,6 +38,15 @@ fun notNullLambda(lambda: (() -> String)?): String { } } +@CompileTimeCalculation +fun nullableCast(str: String?): String { + return try { + str as String + } catch (e: NullPointerException) { + "Null" + } +} + const val a1 = notNullAssertion(1) const val a2 = notNullAssertion(null) const val b1 = notNullAssertionForObject(A()) @@ -46,3 +55,5 @@ const val c1 = notNullAssertionForSomeWrapper(StringBu const val c2 = notNullAssertionForSomeWrapper(null) const val d1 = notNullLambda { "Not null lambda" } const val d2 = notNullLambda(null) +const val e1 = nullableCast("Not null String") +const val e2 = nullableCast(null)