Fix unsound smartcast due to returns(null) in safecall

^KT-26382 Fixed
This commit is contained in:
Dmitry Savvinov
2018-09-05 14:30:32 +03:00
parent 18338f4ea3
commit 051ad0f79e
2 changed files with 18 additions and 5 deletions
@@ -19,12 +19,11 @@ package org.jetbrains.kotlin.contracts
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.contracts.interpretation.ContractInterpretationDispatcher
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.ConditionalEffect
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.contracts.model.functors.*
import org.jetbrains.kotlin.contracts.model.structure.CallComputation
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
import org.jetbrains.kotlin.contracts.model.structure.lift
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -114,6 +113,20 @@ class EffectsExtractingVisitor(
)
}
override fun visitSafeQualifiedExpression(expression: KtSafeQualifiedExpression, data: Unit?): Computation {
val computation = super.visitSafeQualifiedExpression(expression, data)
if (computation === UNKNOWN_COMPUTATION) return computation
// For safecall any clauses of form 'returns(null) -> ...' are incorrect, because safecall can return
// null bypassing function's contract, so we have to filter them out
fun ESEffect.containsReturnsNull(): Boolean =
this == ESReturns(ESConstant.NULL) || this is ConditionalEffect && this.simpleEffect.containsReturnsNull()
val effectsWithoutReturnsNull = computation.effects.filter { !it.containsReturnsNull() }
return CallComputation(computation.type, effectsWithoutReturnsNull)
}
override fun visitBinaryExpression(expression: KtBinaryExpression, data: Unit): Computation {
val left = extractOrGetCached(expression.left ?: return UNKNOWN_COMPUTATION)
val right = extractOrGetCached(expression.right ?: return UNKNOWN_COMPUTATION)
@@ -12,6 +12,6 @@ fun Any.nullWhenString(): Any? {
fun test(x: Int?) {
if (x?.nullWhenString() == null) {
<!DEBUG_INFO_SMARTCAST!>x<!>.length
x.<!UNRESOLVED_REFERENCE!>length<!>
}
}