-
Notifications
You must be signed in to change notification settings - Fork 36
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
Contracts & Harnesses for f{32,64}::to_int_unchecked
#134
Contracts & Harnesses for f{32,64}::to_int_unchecked
#134
Conversation
…-0011-core-nums-yenyunw-f32-to-int-unchecked
f32::to_int_unchecked
to_int_unchecked
…-0011-core-nums-yenyunw-f32-to-int-unchecked
@celinval @carolynzech @zhassan-aws @feliperodri I was able to run f32->i8 harness. One of the checks failed, and I guess it is because the contract is incorrect. Specifically, I think the My confusion is, how can I refer to the max/min of the resulting integer type given that the type parameters accessible from the context of the contract are
|
I found this workaround, inspired by this TypeChecker, and was able to run the f32->i8 harness (see here for unreachables). It took a while (~1.5 minutes) after compilation (
While this workaround might work, I think it would be too messy in the end. I'm wondering if it's possible to have a macro generating functions that check if the float value is in the bound of an integer type, as shown in the example below. However, I think it might cause circular dependency. If there is no better way to make the contract cleaner, maybe we could still proceed with the aforementioned approach. use core::any::type_name;
// Not sure where to place; Not sure if this works.
macro_rules! generate_is_in_range_check {
($floatType:ty, $($intType:ty, $intStr:expr),+) => {
$(
pub fn is_in_range<Int>(num: $floatType) -> bool {
type_name::<$intType>().contains($intStr) && num >= <$intType>::MIN as $floatType && num <= <$intType>::MAX as $floatType
}
)+
}
}
generate_is_in_range_check!(f32, i8, "i8");
Then the contract becomes:
#[requires(self.is_finite() && is_in_range::<Int>(self))] By the way, I also tried the
@celinval @carolynzech @zhassan-aws @feliperodri I would appreciate any ideas and advice! |
I think you should be able to define a macro that matches on the macro_rules! foo {
(u8, ...) => { ... }
(u16, ...) => { ... }
....
} and use it in the contract. |
@feliperodri Thank you for your response! I tried this approach by adding the macro in // mod.rs
pub mod verify {
macro_rules! is_in_range {
(i8, $floatType:ty, $num:ident) => { $num >= i8::MIN as $floatType && $num <= i8::MAX as $floatType };
(i16, $floatType:ty, $num:ident) => { $num >= i16::MIN as $floatType && $num <= i16::MAX as $floatType };
(i32, $floatType:ty, $num:ident) => { $num >= i32::MIN as $floatType && $num <= i32::MAX as $floatType };
(i64, $floatType:ty, $num:ident) => { $num >= i64::MIN as $floatType && $num <= i64::MAX as $floatType };
(i128, $floatType:ty, $num:ident) => { $num >= i128::MIN as $floatType && $num <= i128::MAX as $floatType };
(isize, $floatType:ty, $num:ident) => { $num >= isize::MIN as $floatType && $num <= isize::MAX as $floatType };
(u8, $floatType:ty, $num:ident) => { $num >= u8::MIN as $floatType && $num <= u8::MAX as $floatType };
(u16, $floatType:ty, $num:ident) => { $num >= u16::MIN as $floatType && $num <= u16::MAX as $floatType };
(u32, $floatType:ty, $num:ident) => { $num >= u32::MIN as $floatType && $num <= u32::MAX as $floatType };
(u64, $floatType:ty, $num:ident) => { $num >= u64::MIN as $floatType && $num <= u64::MAX as $floatType };
(u128, $floatType:ty, $num:ident) => { $num >= u128::MIN as $floatType && $num <= u128::MAX as $floatType };
(usize, $floatType:ty, $num:ident) => { $num >= usize::MIN as $floatType && $num <= usize_isize_to_xe_bytes_doc::MAX as $floatType };
}
pub(crate) use is_in_range;
}
// Call in to_int_unchecked contract (f32.rs)
use super::num::verify::is_in_range;
#[requires(self.is_finite() && is_in_range!(Int, Self, self))]
// Result
error: no rules expected `Int`
--> /path/to/repo/vrs/library/core/src/num/f32.rs:1076:49
|
1076 | #[requires(self.is_finite() && is_in_range!(Int, Self, self))]
| ^^^ no rules expected this token in macro call
|
::: /path/to/repo/vrs/library/core/src/num/mod.rs:1693:5
|
1693 | macro_rules! is_in_range {
| ------------------------ when calling this macro
|
note: while trying to match `i8`
--> /path/to/repo/vrs/library/core/src/num/mod.rs:1694:10
|
1694 | (i8, $floatType:ty, $num:ident) => { $num >= i8::MIN as $floatType && $num <= i8::MAX as $floatType };
| |
I see. The other alternative I could think of is to define a trait that provides |
There's also the issue of imprecision when casting an int value to a float. For instance, Not sure what the best approach here is. Both MIRI and (now) Kani have their own internal methods to check if the values are in range (e.g. see rust-lang/miri#1325). I wonder whether those can be somehow exposed in the library. Ideally, there should be a |
Thank you for pointing this out! We will pay attention to it.
Thank you for sharing this. Is it possible to have Kani support this? E.g. expose an Thank you! @zhassan-aws |
Yes, I think it would be possible. This would likely need to be done through providing a trait and its implementation for different float types so that it can be used with the generic |
@zhassan-aws I just filed this Feature Request. Thank you! |
Hi @Yenyun035, we have updated the Kani version in this repo. You should be able to use the new API |
@celinval Thank you! I have run Kani on all harnesses and updated the results in the above. It's ready for review now 👍 To compile, we need to add the |
Can you please add that to verify-rust-std/scripts/run-kani.sh Lines 214 to 216 in 7e8a03d
|
@celinval @tautschnig @carolynzech @feliperodri Could you please review this PR and #163 as well? Thank you! |
@zhassan-aws Thank you. Fixed. |
I found one of the checks failed due to the above error, but the function is indeed used in the contract: #[requires(self.is_finite() && float_to_int_in_range::<Self, Int>(self))] |
Try putting the import under |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!!
Towards #59
(Resolved) Depends on this Kani Issue and this PR, as discussed in this thread in #59
(Resolved) Depends on this Kani Issue and this PR
(Resolved) Waiting for Kani PR#3742 merged into
feature/verify-rust-std
f16 and f128 are in #163
Changes
f{32,64}::to_int_unchecked
(located inlibrary/core/src/num/f{32,64}.rs
)to_int_unchecked
harnessesf{32,64}to_int_unchecked
of each integer typei8
,i16
,i32
,i64
,i128
,isize
,u8
,u16
,u32
,u64
,u128
,usize
--- 12 harnesses in total.Verification Results
To compile, we need to add the
-Z float-lib
flag.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.