Vampire
09/03/2025, 9:59 PMfoo
is non-null?
So why is the second foo
call not working?
fun foo(foo: Any) = null
val foo: String? = null
foo(foo)
check(foo != null)
require(foo != null)
assert(foo != null)
foo(foo)
Youssef Shoaib [MOD]
09/03/2025, 11:28 PMVampire
09/03/2025, 11:44 PMHuib Donkers
09/04/2025, 8:11 AMassert
implies foo
is not null. Assertions may be disabled when compiling, so we can't rely on the condition being true after the assert
statement.Vampire
09/04/2025, 9:24 AMfun <T> T?.markAsNonNull() {
contract {
returns() implies (this@markAsNonNull != null)
}
}
I can tell the compiler that I know better than him (for example due to wrong nullability annotations in Java).
With assert(...)
I'd have expected the same, so that I can just write
fun foo(foo: Any) = null
val foo: String? = null
assert(foo != null)
foo(foo)
instead of
fun foo(foo: Any) = null
val foo: String? = null
foo.markAsNonNull()
foo(foo)
Of course this would fail when assertions get enabled as it actually is null
and the receiver is the actual problem.
But in other cases it would work and should just have the same contract check
and require
have imho.Vampire
09/04/2025, 9:27 AMCLOVIS
09/04/2025, 9:28 AM-ea
there won't be any check, the compiler will not let you write code that assumes that check-that's-not-there would've passed.CLOVIS
09/04/2025, 9:28 AMassert()
for critical things)Youssef Shoaib [MOD]
09/04/2025, 9:29 AMstrongAssert
function that has such contractCLOVIS
09/04/2025, 9:32 AMassume
Vampire
09/04/2025, 9:34 AMmarkAsNonNull
I showed above, that's not the point.
The sense of assert
is, so that you can run tests with -ea
and have the invariants checked, while at production time you can spare the time for those assertions by not enabling them.
But the actual assertions done by the assert
should usually still hold and so imho the compiler should take them into account.
That they are true
whether you enable or disable assertions is the whole point of them.CLOVIS
09/04/2025, 9:39 AMCLOVIS
09/04/2025, 9:40 AM!!
does check, instead of just relying on the platform to throw an exception sometimes laterCLOVIS
09/04/2025, 9:41 AMVampire
09/04/2025, 9:56 AMWell, it's more "I hope it's true but I don't want to spend the time to actually check".No, it's more "I tested all possible edge cases in my tests and know that the condition is always true, so I don't want to spend the actual time for the check at production time". With disabled assertions, this is exactly like a Kotlin contract. The contract can say that
this
is non-null when the method call returns.
The compiler trusts your statement even if you later return null
which is perfectly valid even if in most cases non-sense.
in which situation is a null-check so expensive that you'd want to remove it in productionNowhere. I was hunting a work-around for calling an ill-annotated Java method where Kotlin thought it it non-null while it is nullable. So
foo
actually is null but I want Kotlin to think it is not so that the call is made.
Of course as I said the contract variant here is more appropriate as it works with both, enabled and disabled assertions.
But for the sake of this argument, just assume the null
check is extremely expensive to do.loke
09/04/2025, 12:31 PMVampire
09/04/2025, 12:36 PMVampire
09/04/2025, 12:39 PMassert
and thus assert
should have the same contract as require
and check
.
I just tried to abuse assert
as a work-around for a nullability bug.
But there are definitely checks that you don't want to do at production time, or the assert
feature would be pointless and not exist.
The point is, that the compiler should still follow that contract whether the assertions are actually enabled or disabled.