Support proper behavior in interpreter for cast of null value
This commit is contained in:
committed by
TeamCityServer
parent
c2fc017d96
commit
9638af042d
+18
-10
@@ -456,33 +456,41 @@ class IrInterpreter private constructor(
|
|||||||
val isErased = typeClassifier.owner is IrTypeParameter && !isReified
|
val isErased = typeClassifier.owner is IrTypeParameter && !isReified
|
||||||
val typeOperand = if (isReified) (callStack.getState(typeClassifier) as KTypeState).irType else expression.typeOperand
|
val typeOperand = if (isReified) (callStack.getState(typeClassifier) as KTypeState).irType else expression.typeOperand
|
||||||
|
|
||||||
|
val state = callStack.popState()
|
||||||
when (expression.operator) {
|
when (expression.operator) {
|
||||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
||||||
callStack.popState()
|
// do nothing
|
||||||
// callStack.pushState(getUnitState()) TODO find real use cases for this
|
// callStack.pushState(getUnitState()) TODO find real use cases for this
|
||||||
}
|
}
|
||||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
|
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
|
||||||
if (!isErased && !callStack.peekState()!!.isSubtypeOf(typeOperand)) {
|
when {
|
||||||
val convertibleClassName = callStack.popState().irClass.fqNameWhenAvailable
|
state.isNull() && !typeOperand.isNullable() -> NullPointerException().handleUserException(environment)
|
||||||
ClassCastException("$convertibleClassName cannot be cast to ${typeOperand.render()}").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 -> {
|
IrTypeOperator.SAFE_CAST -> {
|
||||||
if (!isErased && !callStack.peekState()!!.isSubtypeOf(typeOperand)) {
|
when {
|
||||||
callStack.popState()
|
!isErased && !state.isSubtypeOf(typeOperand) -> callStack.pushState(null.toState(irBuiltIns.nothingNType))
|
||||||
callStack.pushState(null.toState(irBuiltIns.nothingNType))
|
else -> callStack.pushState(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IrTypeOperator.INSTANCEOF -> {
|
IrTypeOperator.INSTANCEOF -> {
|
||||||
val isInstance = callStack.popState().isSubtypeOf(typeOperand) || isErased
|
val isInstance = state.isSubtypeOf(typeOperand) || isErased
|
||||||
callStack.pushState(isInstance.toState(irBuiltIns.booleanType))
|
callStack.pushState(isInstance.toState(irBuiltIns.booleanType))
|
||||||
}
|
}
|
||||||
IrTypeOperator.NOT_INSTANCEOF -> {
|
IrTypeOperator.NOT_INSTANCEOF -> {
|
||||||
val isInstance = callStack.popState().isSubtypeOf(typeOperand) || isErased
|
val isInstance = state.isSubtypeOf(typeOperand) || isErased
|
||||||
callStack.pushState((!isInstance).toState(irBuiltIns.booleanType))
|
callStack.pushState((!isInstance).toState(irBuiltIns.booleanType))
|
||||||
}
|
}
|
||||||
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
||||||
|
when {
|
||||||
|
state.isNull() && !typeOperand.isNullable() -> NullPointerException().handleUserException(environment)
|
||||||
|
else -> callStack.pushState(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> TODO("${expression.operator} not implemented")
|
else -> TODO("${expression.operator} not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -290,6 +290,7 @@ internal object AssertIntrinsic : IntrinsicBase() {
|
|||||||
|
|
||||||
val messageLambda = environment.callStack.getState(irFunction.valueParameters.last().symbol) as KFunctionState
|
val messageLambda = environment.callStack.getState(irFunction.valueParameters.last().symbol) as KFunctionState
|
||||||
val function = messageLambda.irFunction as IrSimpleFunction
|
val function = messageLambda.irFunction as IrSimpleFunction
|
||||||
|
environment.callStack.loadUpValues(messageLambda)
|
||||||
val call = IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.returnType, function.symbol)
|
val call = IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.returnType, function.symbol)
|
||||||
|
|
||||||
return listOf(customEvaluateInstruction(irFunction, environment), CompoundInstruction(call))
|
return listOf(customEvaluateInstruction(irFunction, environment), CompoundInstruction(call))
|
||||||
|
|||||||
@@ -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 = <!EVALUATED: `Value isn't null`!>notNullAssertion(1)<!>
|
const val a1 = <!EVALUATED: `Value isn't null`!>notNullAssertion(1)<!>
|
||||||
const val a2 = <!EVALUATED: `Value is null`!>notNullAssertion(null)<!>
|
const val a2 = <!EVALUATED: `Value is null`!>notNullAssertion(null)<!>
|
||||||
const val b1 = <!EVALUATED: `Value isn't null`!>notNullAssertionForObject(A())<!>
|
const val b1 = <!EVALUATED: `Value isn't null`!>notNullAssertionForObject(A())<!>
|
||||||
@@ -46,3 +55,5 @@ const val c1 = <!EVALUATED: `Some text`!>notNullAssertionForSomeWrapper(StringBu
|
|||||||
const val c2 = <!EVALUATED: `Value is null`!>notNullAssertionForSomeWrapper(null)<!>
|
const val c2 = <!EVALUATED: `Value is null`!>notNullAssertionForSomeWrapper(null)<!>
|
||||||
const val d1 = <!EVALUATED: `Not null lambda`!>notNullLambda { "Not null lambda" }<!>
|
const val d1 = <!EVALUATED: `Not null lambda`!>notNullLambda { "Not null lambda" }<!>
|
||||||
const val d2 = <!EVALUATED: `Lambda is null`!>notNullLambda(null)<!>
|
const val d2 = <!EVALUATED: `Lambda is null`!>notNullLambda(null)<!>
|
||||||
|
const val e1 = <!EVALUATED: `Not null String`!>nullableCast("Not null String")<!>
|
||||||
|
const val e2 = <!EVALUATED: `Null`!>nullableCast(null)<!>
|
||||||
|
|||||||
Reference in New Issue
Block a user