Examples¶
This page walks through several example files to illustrate how perlchecker works in practice.
Basic Verification¶
File: examples/00_identity_with_pre.pl
# sig: (I64) -> I64
# pre: $x >= 0
# post: $result == $x
sub identity_with_pre {
my ($x) = @_;
return $x;
}
$ perlchecker check examples/00_identity_with_pre.pl
✔ identity_with_pre: verified
The function returns its input unchanged. The precondition constrains
$x to non-negative values, and the postcondition states the result
equals $x. There is one path, and the solver confirms the postcondition
holds for all valid inputs.
Finding a Counterexample¶
File: examples/04_counterexample.pl
# sig: (I64) -> I64
# post: $result > $x
sub counterexample {
my ($x) = @_;
if ($x >= 0) {
return $x;
} else {
return $x + 1;
}
}
$ perlchecker check examples/04_counterexample.pl
✘ counterexample: counterexample found
Function counterexample failed:
x = 0
The postcondition claims the result is strictly greater than the input. On
the $x >= 0 branch, the function returns $x itself — which is not
greater than $x. The solver finds x = 0 as a concrete witness.
Safe Division¶
File: examples/09_safe_division.pl
# sig: (I64, I64) -> I64
# pre: $y != 0
# post: $result == $x / $y
sub safe_division {
my ($x, $y) = @_;
return $x / $y;
}
$ perlchecker check examples/09_safe_division.pl
✔ safe_division: verified
The precondition $y != 0 guards against division by zero. The verifier
prunes any path where $y == 0, so the division is always safe. Without
the precondition, verification would fail because the zero-divisor path
would be discarded as invalid, leaving no valid paths to check.
String Operations¶
File: examples/12_string_concat_length.pl
# sig: (Str, Str) -> Str
# post: length($result) == length($x) + length($y)
sub string_concat_length {
my ($x, $y) = @_;
return $x . $y;
}
$ perlchecker check examples/12_string_concat_length.pl
✔ string_concat_length: verified
The postcondition states that concatenating two strings produces a result whose length equals the sum of the input lengths. The Z3 string theory confirms this holds for all strings (up to the 32-character bound).
Arrays¶
File: examples/21_array_read_verified.pl
# sig: (Array<I64>, I64) -> I64
# post: $result == $arr[$i]
sub array_read_verified {
my ($arr, $i) = @_;
return $arr[$i];
}
$ perlchecker check examples/21_array_read_verified.pl
✔ array_read_verified: verified
Arrays are modeled using Z3’s Array theory. Reading an element and returning
it trivially satisfies the postcondition $result == $arr[$i].
Function Calls¶
File: examples/27_function_call_verified.pl
# sig: (I64) -> I64
# post: $result == $x + 1
sub inc {
my ($x) = @_;
return $x + 1;
}
# sig: (I64) -> I64
# post: $result == $x + 1
sub function_call_verified {
my ($x) = @_;
return inc($x);
}
$ perlchecker check examples/27_function_call_verified.pl
✔ inc: verified
✔ function_call_verified: verified
Both functions are verified. function_call_verified calls inc,
which is inlined during verification. The verifier proves that inc($x)
returns $x + 1, satisfying the caller’s postcondition.
Loops¶
File: examples/30_while_verified.pl
# sig: (I64) -> I64
# pre: $x >= 0 && $x <= 5
# post: $result == 0
sub while_verified {
my ($x) = @_;
while ($x > 0) {
$x = $x - 1;
}
return $x;
}
$ perlchecker check examples/30_while_verified.pl
✔ while_verified: verified
The precondition bounds $x to [0, 5], ensuring the loop terminates
within the default unroll depth of 5. The loop counts down to 0, and the
postcondition $result == 0 is verified across all paths.
Note
Without the precondition bounding $x, this function would fail with
a “loop unroll bound exceeded” error because the loop could iterate
arbitrarily many times.