Skip to content

Commit d178441

Browse files
jesyspaclaude
andcommitted
Add verification tests for nullable type operations
7 new verification tests covering: - Elvis operator with null input (proves default value is returned) - Elvis operator with non-null input (proves passthrough) - Nullable passthrough (proves identity preservation) - Null literal return (proves result is null) - Null comparison on null value (proves true) - Non-null comparison on null value (proves false) - Smart cast in if-else branch (proves type narrowing) All tests pass Viper verification with z3. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d7f744c commit d178441

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

formver.compiler-plugin/test-gen/org/jetbrains/kotlin/formver/plugin/runners/FirLightTreeFormVerPluginDiagnosticsTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,12 @@ public void testNullability() {
494494
runTest("formver.compiler-plugin/testData/diagnostics/verification/nullability.kt");
495495
}
496496

497+
@Test
498+
@TestMetadata("nullability_operations.kt")
499+
public void testNullability_operations() {
500+
runTest("formver.compiler-plugin/testData/diagnostics/verification/nullability_operations.kt");
501+
}
502+
497503
@Test
498504
@TestMetadata("stdlib_replacement_tests.kt")
499505
public void testStdlib_replacement_tests() {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/nullability_operations.kt:(124,142): info: Generated Viper text for elvisDefaultOnNull:
2+
method f$elvisDefaultOnNull$TF$() returns (ret$0: Ref)
3+
ensures df$rt$isSubtype(df$rt$typeOf(ret$0), df$rt$intType())
4+
ensures df$rt$intFromRef(ret$0) == 42
5+
{
6+
var l0$x: Ref
7+
l0$x := df$rt$nullValue()
8+
if (l0$x != df$rt$nullValue()) {
9+
ret$0 := l0$x
10+
} else {
11+
ret$0 := df$rt$intToRef(42)}
12+
goto lbl$ret$0
13+
label lbl$ret$0
14+
}
15+
16+
/nullability_operations.kt:(319,338): info: Generated Viper text for nullablePassthrough:
17+
method f$nullablePassthrough$TF$NT$Int(p$x: Ref) returns (ret$0: Ref)
18+
ensures df$rt$isSubtype(df$rt$typeOf(ret$0), df$rt$nullable(df$rt$intType()))
19+
ensures ret$0 == p$x
20+
{
21+
inhale df$rt$isSubtype(df$rt$typeOf(p$x), df$rt$nullable(df$rt$intType()))
22+
ret$0 := p$x
23+
goto lbl$ret$0
24+
label lbl$ret$0
25+
}
26+
27+
/nullability_operations.kt:(472,489): info: Generated Viper text for returnNullLiteral:
28+
method f$returnNullLiteral$TF$() returns (ret$0: Ref)
29+
ensures df$rt$isSubtype(df$rt$typeOf(ret$0), df$rt$nullable(df$rt$intType()))
30+
ensures ret$0 == df$rt$nullValue()
31+
{
32+
ret$0 := df$rt$nullValue()
33+
goto lbl$ret$0
34+
label lbl$ret$0
35+
}
36+
37+
/nullability_operations.kt:(637,662): info: Generated Viper text for nullComparisonReturnsTrue:
38+
method f$nullComparisonReturnsTrue$TF$() returns (ret$0: Ref)
39+
ensures df$rt$isSubtype(df$rt$typeOf(ret$0), df$rt$boolType())
40+
ensures df$rt$boolFromRef(ret$0) == true
41+
{
42+
var l0$x: Ref
43+
l0$x := df$rt$nullValue()
44+
ret$0 := df$rt$boolToRef(l0$x == df$rt$nullValue())
45+
goto lbl$ret$0
46+
label lbl$ret$0
47+
}
48+
49+
/nullability_operations.kt:(848,877): info: Generated Viper text for nonNullComparisonReturnsFalse:
50+
method f$nonNullComparisonReturnsFalse$TF$() returns (ret$0: Ref)
51+
ensures df$rt$isSubtype(df$rt$typeOf(ret$0), df$rt$boolType())
52+
ensures df$rt$boolFromRef(ret$0) == false
53+
{
54+
var l0$x: Ref
55+
l0$x := df$rt$nullValue()
56+
ret$0 := sp$notBool(df$rt$boolToRef(l0$x == df$rt$nullValue()))
57+
goto lbl$ret$0
58+
label lbl$ret$0
59+
}
60+
61+
/nullability_operations.kt:(1063,1079): info: Generated Viper text for elvisWithNonNull:
62+
method f$elvisWithNonNull$TF$T$Int(p$x: Ref) returns (ret$0: Ref)
63+
ensures df$rt$isSubtype(df$rt$typeOf(ret$0), df$rt$intType())
64+
ensures df$rt$intFromRef(ret$0) == df$rt$intFromRef(p$x)
65+
{
66+
var l0$y: Ref
67+
inhale df$rt$isSubtype(df$rt$typeOf(p$x), df$rt$intType())
68+
l0$y := p$x
69+
if (l0$y != df$rt$nullValue()) {
70+
ret$0 := l0$y
71+
} else {
72+
ret$0 := df$rt$intToRef(0)}
73+
goto lbl$ret$0
74+
label lbl$ret$0
75+
}
76+
77+
/nullability_operations.kt:(1251,1268): info: Generated Viper text for smartcastInBranch:
78+
method f$smartcastInBranch$TF$NT$Int(p$n: Ref) returns (ret$0: Ref)
79+
ensures df$rt$isSubtype(df$rt$typeOf(ret$0), df$rt$intType())
80+
{
81+
inhale df$rt$isSubtype(df$rt$typeOf(p$n), df$rt$nullable(df$rt$intType()))
82+
if (!(p$n == df$rt$nullValue())) {
83+
ret$0 := p$n
84+
goto lbl$ret$0
85+
}
86+
ret$0 := df$rt$intToRef(0)
87+
goto lbl$ret$0
88+
label lbl$ret$0
89+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import org.jetbrains.kotlin.formver.plugin.*
2+
3+
// Verify that elvis returns the default when input is null
4+
@AlwaysVerify
5+
fun <!VIPER_TEXT!>elvisDefaultOnNull<!>(): Int {
6+
postconditions<Int> {
7+
it == 42
8+
}
9+
val x: Int? = null
10+
return x ?: 42
11+
}
12+
13+
// Verify that nullable passthrough preserves identity
14+
@AlwaysVerify
15+
fun <!VIPER_TEXT!>nullablePassthrough<!>(x: Int?): Int? {
16+
postconditions<Int?> {
17+
it == x
18+
}
19+
return x
20+
}
21+
22+
// Verify returning null literal
23+
@AlwaysVerify
24+
fun <!VIPER_TEXT!>returnNullLiteral<!>(): Int? {
25+
postconditions<Int?> {
26+
it == null
27+
}
28+
return null
29+
}
30+
31+
// Verify null comparison on a known-null value
32+
@AlwaysVerify
33+
fun <!VIPER_TEXT!>nullComparisonReturnsTrue<!>(): Boolean {
34+
postconditions<Boolean> {
35+
it == true
36+
}
37+
val x: Int? = null
38+
return x == null
39+
}
40+
41+
// Verify non-null comparison on a known-null value
42+
@AlwaysVerify
43+
fun <!VIPER_TEXT!>nonNullComparisonReturnsFalse<!>(): Boolean {
44+
postconditions<Boolean> {
45+
it == false
46+
}
47+
val x: Int? = null
48+
return x != null
49+
}
50+
51+
// Verify elvis with non-null input passes through
52+
@AlwaysVerify
53+
fun <!VIPER_TEXT!>elvisWithNonNull<!>(x: Int): Int {
54+
postconditions<Int> {
55+
it == x
56+
}
57+
val y: Int? = x
58+
return y ?: 0
59+
}
60+
61+
// Verify smart cast after null check in if-else
62+
@AlwaysVerify
63+
fun <!VIPER_TEXT!>smartcastInBranch<!>(n: Int?): Int {
64+
if (n != null) {
65+
return n
66+
}
67+
return 0
68+
}

0 commit comments

Comments
 (0)