forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 58
Add loop invariants and harnesses for DecimalSeq functions #439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
thanhnguyen-aws
wants to merge
24
commits into
model-checking:main
Choose a base branch
from
thanhnguyen-aws:decseqinvariant
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+85
−1
Open
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
7e52e12
add loop invariants & harnesses for DecimalSeq functions
thanhnguyen-aws 1402d50
remove unneccessary assume
thanhnguyen-aws 7f2fd77
Merge branch 'main' into decseqinvariant
thanhnguyen-aws 874421e
fix format + add comments
thanhnguyen-aws b5ee3b2
Merge branch 'decseqinvariant' of https://github.com/thanhnguyen-aws/…
thanhnguyen-aws 13af0ca
Update library/core/src/num/dec2flt/decimal_seq.rs
thanhnguyen-aws d2eb714
Update library/core/src/num/dec2flt/decimal_seq.rs
thanhnguyen-aws c8d98b3
fix impl Arbitrary
thanhnguyen-aws 5753d41
change kaniindex to kani::index
thanhnguyen-aws e9b0c54
Merge branch 'main' into decseqinvariant
thanhnguyen-aws 425e153
Merge branch 'main' into decseqinvariant
thanhnguyen-aws 5cd5a6c
add forall + change kani to safety
thanhnguyen-aws 0ff0c95
fix format
thanhnguyen-aws e8ac050
kani::forall -> forall
thanhnguyen-aws f71ef69
add -Z quantifier
thanhnguyen-aws f74f6e8
add assumption
thanhnguyen-aws ebe6ace
Update library/core/src/num/dec2flt/decimal_seq.rs
tautschnig 4559232
Update library/core/src/num/dec2flt/decimal_seq.rs
tautschnig c876cd6
Update scripts/run-kani.sh
tautschnig f9e0a67
Revert "Update scripts/run-kani.sh"
tautschnig b8b73bc
write_index, again
tautschnig 8ccd693
safety -> kani
thanhnguyen-aws 038c707
Extended invariants
tautschnig 0f0f4b1
Merge branch 'main' into decseqinvariant
feliperodri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,12 @@ | |
//! algorithm can be found in "ParseNumberF64 by Simple Decimal Conversion", | ||
//! available online: <https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html>. | ||
|
||
use safety::ensures; | ||
|
||
#[cfg(kani)] | ||
use crate::forall; | ||
#[cfg(kani)] | ||
use crate::kani; | ||
use crate::num::dec2flt::common::{ByteSlice, is_8digits}; | ||
|
||
/// A decimal floating-point number, represented as a sequence of decimal digits. | ||
|
@@ -83,6 +89,7 @@ impl DecimalSeq { | |
// | ||
// Trim is only called in `right_shift` and `left_shift`. | ||
debug_assert!(self.num_digits <= Self::MAX_DIGITS); | ||
#[cfg_attr(kani, kani::loop_invariant(self.num_digits <= Self::MAX_DIGITS))] | ||
while self.num_digits != 0 && self.digits[self.num_digits - 1] == 0 { | ||
self.num_digits -= 1; | ||
} | ||
|
@@ -98,6 +105,7 @@ impl DecimalSeq { | |
let dp = self.decimal_point as usize; | ||
let mut n = 0_u64; | ||
|
||
#[cfg_attr(kani, kani::loop_invariant(n < 10u64.pow(kani::index as u32)))] | ||
for i in 0..dp { | ||
n *= 10; | ||
if i < self.num_digits { | ||
|
@@ -130,6 +138,14 @@ impl DecimalSeq { | |
let mut write_index = self.num_digits + num_new_digits; | ||
let mut n = 0_u64; | ||
|
||
#[cfg_attr(kani, kani::loop_invariant(read_index <= Self::MAX_DIGITS && | ||
write_index == read_index + num_new_digits && | ||
n < 10u64 << (shift - 1) && | ||
(n == 0 || (shift & 63) <= 3 || (shift & 63) >= 61 || write_index > 0) && | ||
self.num_digits <= Self::MAX_DIGITS && | ||
self.decimal_point <= self.num_digits as i32 && | ||
forall!(|i in (0,DecimalSeq::MAX_DIGITS)| self.digits[i] <= 9) | ||
))] | ||
while read_index != 0 { | ||
read_index -= 1; | ||
write_index -= 1; | ||
|
@@ -144,7 +160,14 @@ impl DecimalSeq { | |
n = quotient; | ||
} | ||
|
||
#[cfg_attr(kani, kani::loop_invariant(self.num_digits <= Self::MAX_DIGITS && self.decimal_point <= self.num_digits as i32))] | ||
// TODO: should add invariant along the lines of | ||
// (n == 0 || write_index > 0) && (prev(n) as usize + 9) / 10 <= prev(write_index))] | ||
// to avoid the below kani::assume | ||
Comment on lines
+164
to
+166
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we still need this? @thanhnguyen-aws |
||
while n > 0 { | ||
// true but hard to write proof with kani currently | ||
#[cfg(kani)] | ||
kani::assume(write_index > 0); | ||
write_index -= 1; | ||
let quotient = n / 10; | ||
let remainder = n - (10 * quotient); | ||
|
@@ -171,13 +194,15 @@ impl DecimalSeq { | |
let mut read_index = 0; | ||
let mut write_index = 0; | ||
let mut n = 0_u64; | ||
#[cfg_attr(kani, kani::loop_invariant( n == 0 || (read_index > 0 && read_index <= self.num_digits + 64 - n.leading_zeros() as usize)))] | ||
while (n >> shift) == 0 { | ||
if read_index < self.num_digits { | ||
n = (10 * n) + self.digits[read_index] as u64; | ||
read_index += 1; | ||
} else if n == 0 { | ||
return; | ||
} else { | ||
#[cfg_attr(kani, kani::loop_invariant(n > 0 && read_index <= self.num_digits + 64 - n.leading_zeros() as usize && read_index > 0))] | ||
while (n >> shift) == 0 { | ||
n *= 10; | ||
read_index += 1; | ||
|
@@ -194,13 +219,18 @@ impl DecimalSeq { | |
return; | ||
} | ||
let mask = (1_u64 << shift) - 1; | ||
#[cfg_attr(kani, kani::loop_invariant(self.num_digits <= Self::MAX_DIGITS && | ||
write_index < read_index && | ||
write_index < Self::MAX_DIGITS - self.num_digits.saturating_sub(read_index) | ||
))] | ||
while read_index < self.num_digits { | ||
let new_digit = (n >> shift) as u8; | ||
n = (10 * (n & mask)) + self.digits[read_index] as u64; | ||
read_index += 1; | ||
self.digits[write_index] = new_digit; | ||
write_index += 1; | ||
} | ||
#[cfg_attr(kani, kani::loop_invariant(write_index <= Self::MAX_DIGITS))] | ||
while n > 0 { | ||
let new_digit = (n >> shift) as u8; | ||
n = 10 * (n & mask); | ||
|
@@ -297,6 +327,7 @@ pub fn parse_decimal_seq(mut s: &[u8]) -> DecimalSeq { | |
d | ||
} | ||
|
||
#[safety::ensures(|result| (shift & 63) <= 3 || (shift & 63) >= 61 || *result > 0)] | ||
fn number_of_digits_decimal_left_shift(d: &DecimalSeq, mut shift: usize) -> usize { | ||
#[rustfmt::skip] | ||
const TABLE: [u16; 65] = [ | ||
|
@@ -363,6 +394,7 @@ fn number_of_digits_decimal_left_shift(d: &DecimalSeq, mut shift: usize) -> usiz | |
let pow5_b = (0x7FF & x_b) as usize; | ||
let pow5 = &TABLE_POW5[pow5_a..]; | ||
|
||
#[cfg_attr(kani, kani::loop_invariant(num_new_digits > 1 || shift <= 3 || shift >= 61))] | ||
for (i, &p5) in pow5.iter().enumerate().take(pow5_b - pow5_a) { | ||
if i >= d.num_digits { | ||
return num_new_digits - 1; | ||
|
@@ -377,3 +409,55 @@ fn number_of_digits_decimal_left_shift(d: &DecimalSeq, mut shift: usize) -> usiz | |
|
||
num_new_digits | ||
} | ||
|
||
#[cfg(kani)] | ||
#[unstable(feature = "kani", issue = "none")] | ||
pub mod decimal_seq_verify { | ||
use super::*; | ||
|
||
impl kani::Arbitrary for DecimalSeq { | ||
fn any() -> DecimalSeq { | ||
let mut ret = DecimalSeq { | ||
num_digits: kani::any_where(|x| *x <= DecimalSeq::MAX_DIGITS), | ||
decimal_point: kani::any_where(|x| *x >= 0), | ||
truncated: kani::any(), | ||
digits: kani::any(), | ||
}; | ||
kani::assume(ret.decimal_point <= ret.num_digits as i32); | ||
kani::assume(forall!(|i in (0,DecimalSeq::MAX_DIGITS)| ret.digits[i] <= 9)); | ||
ret | ||
} | ||
} | ||
|
||
#[kani::proof] | ||
fn check_round() { | ||
let mut a: DecimalSeq = kani::any(); | ||
a.round(); | ||
} | ||
|
||
#[kani::proof] | ||
fn check_number_of_digits_decimal_left_shift() { | ||
let mut a: DecimalSeq = kani::any(); | ||
let shift: usize = kani::any_where(|x| *x > 0 && *x <= 60); | ||
let n = number_of_digits_decimal_left_shift(&a, shift); | ||
// 19 is the greatest number x such that 10u64^x does not overflow | ||
// It is also TABLE.max << 11 | ||
assert!(n <= 19); | ||
thanhnguyen-aws marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert!(n == 19 || 1u64 << shift < 10u64.pow(n as u32 + 1)) | ||
} | ||
|
||
#[kani::proof] | ||
fn check_right_shift() { | ||
let mut a: DecimalSeq = kani::any(); | ||
//This function is called in parse_long_mantissa function (slow.rs), in which the maximum of shift is 60 | ||
let shift: usize = kani::any_where(|x| *x > 0 && *x <= 60); | ||
a.right_shift(shift); | ||
} | ||
|
||
#[kani::proof] | ||
fn check_left_shift() { | ||
let mut a: DecimalSeq = kani::any(); | ||
let shift: usize = kani::any_where(|x| *x > 0 && *x <= 60); | ||
a.left_shift(shift); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.