Am I wrong in expecting a smart cast in both cases...
# announcements
g
Am I wrong in expecting a smart cast in both cases? 🤔
Copy code
fun main() {
    val foo: Foo? = Foo("")

    if (foo?.bar != null && foo.bar.isNotEmpty()) {
        println(foo.bar.length) // smart cast
    }
    if (!foo?.bar.isNullOrEmpty()) {
        println(foo.bar.length) // no smart cast
    }
}

class Foo(val bar: String?)
o
In the second case you can’t differentiate between foo being null, and bar being null, so you’d still need to do the check, or store
foo?.bar
to an intermediate val and do the check there
f
exactly, isNullOrEmpty contract only apply to bar
g
if bar is not null how can foo be null?
f
but when it's null Kotlin can't know which one is null foo or bar, so the contract dn't apply
g
but I am checking that it’s not null
neither foo nor bar is smart cast
case 3 is similar and the ide suggests to fold it to
foo?.bar != null
Also, the contract explicitly applies when return is false
Copy code
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.length == 0
}
o
Interesting 🤔 I wonder exactly what’s making the contract not work without the intermediate val
🤨 1
I think it’s because the
?.
in the expression on which the contract is supposed to work is ruining it
And
foo?.bar != null
works because you’re not calling any function
Possibly something about call stacks? Wild guess here
🤷‍♂️ 1
d
@gian you are right, there should be smartcasts, but it's missing Current contract system has some limitations, because it was implemented as ad-hoc subsystem of data flow analyzer, that is responsible for calculation of smartcasts, so it can't resolve cases like that In new compiler that was announced on KotlinConf we reimplement contract system, so it's deeply integrated with DFA, and I just checked that your case works with it
😄 4
g
Awesome, thanks!