Make 'is'-operator more stupid

Consider following expression: 'call() is Foo'. Suppose that we know
something about the 'call()', e.g. 'returns(foo) -> <condition>'

Previously, we've tried to re-use knowledge about 'call()', constructing
some smart clause, like 'returns(true) -> foo is Foo && <condition>'.

The conceptual error here is that *we can't* argue that <condition>
holds. Imagine that 'call()' actually has unspecified 'returns(foo2) ->
<!condition>', and 'foo2 is Foo' also holds. Then we would get
'returns(true) -> foo2 is Foo && <condition>' <=> 'returns(true) ->
<condition>' for the whole call, which is not correct.

More concrete example would be something like:
'if (!x.isNullOrEmpty() is Boolean)'

^KT-27241 Fixed
This commit is contained in:
Dmitry Savvinov
2019-01-15 11:37:37 +03:00
parent 04ff2a3ee7
commit e483f83666
4 changed files with 10 additions and 17 deletions
@@ -29,22 +29,17 @@ class IsFunctor(val type: KotlinType, val isNegated: Boolean) : AbstractReducing
fun invokeWithArguments(arg: Computation): List<ESEffect> {
return if (arg is ESValue)
invokeWithValue(arg, null)
invokeWithValue(arg)
else
arg.effects.flatMap {
if (it !is ConditionalEffect || it.simpleEffect !is ESReturns || it.simpleEffect.value == ESConstant.WILDCARD)
listOf(it)
else
invokeWithValue(it.simpleEffect.value, it.condition)
}
emptyList()
}
private fun invokeWithValue(value: ESValue, additionalCondition: ESExpression?): List<ConditionalEffect> {
private fun invokeWithValue(value: ESValue): List<ConditionalEffect> {
val trueIs = ESIs(value, this)
val falseIs = ESIs(value, IsFunctor(type, isNegated.not()))
val trueResult = ConditionalEffect(trueIs.and(additionalCondition), ESReturns(true.lift()))
val falseResult = ConditionalEffect(falseIs.and(additionalCondition), ESReturns(false.lift()))
val trueResult = ConditionalEffect(trueIs, ESReturns(true.lift()))
val falseResult = ConditionalEffect(falseIs, ESReturns(false.lift()))
return listOf(trueResult, falseResult)
}
}
@@ -6,6 +6,6 @@ import kotlin.contracts.*
fun f3(value: String?) {
if (<!USELESS_IS_CHECK!>!value.isNullOrEmpty() is Boolean<!>) {
<!DEBUG_INFO_SMARTCAST!>value<!>.length
value<!UNSAFE_CALL!>.<!>length
}
}
@@ -1,4 +1,3 @@
package
public fun myIf(/*0*/ cond: kotlin.Boolean): kotlin.Any
public fun test(/*0*/ x: kotlin.Any?): kotlin.Unit
public fun f3(/*0*/ value: kotlin.String?): kotlin.Unit
@@ -7,7 +7,6 @@
* SECTIONS: contracts, analysis, smartcasts
* NUMBER: 15
* DESCRIPTION: Check smartcasts working if type checking for contract function is used
* UNEXPECTED BEHAVIOUR
* ISSUES: KT-27241
*/
@@ -40,20 +39,20 @@ import contracts.*
// TESTCASE NUMBER: 1
fun case_1(value: Any) {
if (contracts.case_1_2(contracts.case_1_1(value is Char))) {
println(<!DEBUG_INFO_SMARTCAST!>value<!>.category)
println(value.<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>category<!>)
}
}
// TESTCASE NUMBER: 2
fun case_2(value: Any) {
if (contracts.case_2(value is Char) is Boolean) {
println(<!DEBUG_INFO_SMARTCAST!>value<!>.category)
println(value.<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>category<!>)
}
}
// TESTCASE NUMBER: 3
fun case_3(value: String?) {
if (<!USELESS_IS_CHECK!>!value.isNullOrEmpty() is Boolean<!>) {
<!DEBUG_INFO_SMARTCAST!>value<!>.length
value<!UNSAFE_CALL!>.<!>length
}
}