From 051ad0f79e5a44095726a727f30b85b3a4e28cb9 Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Wed, 5 Sep 2018 14:30:32 +0300 Subject: [PATCH] Fix unsound smartcast due to returns(null) in safecall ^KT-26382 Fixed --- .../contracts/EffectsExtractingVisitor.kt | 21 +++++++++++++++---- .../smartcasts/safecallAndReturnsNull.kt | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt index c4811e108f9..519b73f6987 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/contracts/EffectsExtractingVisitor.kt @@ -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) diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/safecallAndReturnsNull.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/safecallAndReturnsNull.kt index cd3cb396769..96916e514e9 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/safecallAndReturnsNull.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/safecallAndReturnsNull.kt @@ -12,6 +12,6 @@ fun Any.nullWhenString(): Any? { fun test(x: Int?) { if (x?.nullWhenString() == null) { - x.length + x.length } } \ No newline at end of file