diff --git a/src/verusspec/bigint.rs b/src/verusspec/bigint.rs index 11f4afa..8e419ae 100644 --- a/src/verusspec/bigint.rs +++ b/src/verusspec/bigint.rs @@ -12,11 +12,13 @@ clippy::pattern_type_mismatch )] +#[cfg(verus_keep_ghost)] use num_bigint::BigInt; use vstd::prelude::*; verus! { +#[cfg(verus_keep_ghost)] #[verifier::external_type_specification] #[verifier::external_body] pub struct ExNumBigInt(num_bigint::BigInt); @@ -26,10 +28,12 @@ pub assume_specification[ ::clone ](n: &BigInt) -> (res: BigInt res == n, ; +#[cfg(verus_keep_ghost)] pub trait BigIntAdditionalSpecFns { spec fn view(&self) -> int; } +#[cfg(verus_keep_ghost)] impl BigIntAdditionalSpecFns for BigInt { uninterp spec fn view(&self) -> int; } @@ -68,215 +72,6 @@ pub assume_specification[ >::from ](u: u128) res@ == u, ; -// ToPrimitive - -#[verifier::external_trait_specification] -#[verifier::external_trait_extension(ToPrimitiveSpec via ToPrimitiveSpecImpl)] -pub trait ExToPrimitive { - type ExternalTraitSpecificationFor: num_traits::ToPrimitive; - - spec fn obeys_to_primitive_spec() -> bool; - - spec fn spec_to_int(&self) -> Option; - - fn to_isize(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(isize::MIN <= n <= isize::MAX), - }, - default_ensures - true, - ; - - fn to_i8(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(i8::MIN <= n <= i8::MAX), - }, - default_ensures - true, - ; - - fn to_i16(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(i16::MIN <= n <= i16::MAX), - }, - default_ensures - true, - ; - - fn to_i32(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(i32::MIN <= n <= i32::MAX), - }, - default_ensures - true, - ; - - fn to_i64(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(i64::MIN <= n <= i64::MAX), - }, - ; - - fn to_i128(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(i128::MIN <= n <= i128::MAX), - }, - default_ensures - true, - ; - - fn to_usize(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(usize::MIN <= n <= usize::MAX), - }, - default_ensures - true, - ; - - fn to_u8(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(u8::MIN <= n <= u8::MAX), - }, - default_ensures - true, - ; - - fn to_u16(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(u16::MIN <= n <= u16::MAX), - }, - default_ensures - true, - ; - - fn to_u32(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(u32::MIN <= n <= u32::MAX), - }, - default_ensures - true, - ; - - fn to_u64(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(u64::MIN <= n <= u64::MAX), - }, - ; - - fn to_u128(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> - match (self.spec_to_int(), res) { - (None, None) => true, - (None, Some(_)) => false, - (Some(n1), Some(n2)) => n1 == n2, - (Some(n), None) => !(u128::MIN <= n <= u128::MAX), - }, - default_ensures - true, - ; - - spec fn spec_to_f32(&self) -> Option; - - fn to_f32(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> res == self.spec_to_f32(), - default_ensures - true, - ; - - spec fn spec_to_f64(&self) -> Option; - - fn to_f64(&self) -> (res: Option) - ensures - Self::obeys_to_primitive_spec() ==> res == self.spec_to_f64(), - default_ensures - true, - ; -} - -impl ToPrimitiveSpecImpl for num_bigint::BigInt -{ - open spec fn obeys_to_primitive_spec() -> bool - { - true - } - - open spec fn spec_to_int(&self) -> Option - { - Some(self@) - } - - uninterp spec fn spec_to_f32(&self) -> Option; - - uninterp spec fn spec_to_f64(&self) -> Option; -} - -// These are the methods of ToPrimitive that BigInt implements because there is no default in ToPrimitive -pub assume_specification[ ::to_i64 ](x: &BigInt) -> (res: Option); -pub assume_specification[ ::to_u64 ](x: &BigInt) -> (res: Option); - -// These are the methods of ToPrimitive that BigInt overrides the defaults for because they'd otherwise be wrong -pub assume_specification[ ::to_i128 ](x: &BigInt) -> (res: Option); -pub assume_specification[ ::to_u128 ](x: &BigInt) -> (res: Option); -pub assume_specification[ ::to_f32 ](x: &BigInt) -> (res: Option); -pub assume_specification[ ::to_f64 ](x: &BigInt) -> (res: Option); - // Negation pub assume_specification[ ::neg ](x: BigInt) -> (y: BigInt) @@ -663,3 +458,221 @@ pub assume_specification<'a>[ >::div ](x: BigInt, ; } // end verus! + +// Verus's encoding of ToPrimitive relies on an unstable feature +// `sized_hierarchy`, so we can only talk about it when verifying. +// So, we wrap it all in `#[cfg(verus_keep_ghost)]`. + +#[cfg(verus_keep_ghost)] +verus! { + +// ToPrimitive + +#[verifier::external_trait_specification] +#[verifier::external_trait_extension(ToPrimitiveSpec via ToPrimitiveSpecImpl)] +pub trait ExToPrimitive { + type ExternalTraitSpecificationFor: num_traits::ToPrimitive; + + spec fn obeys_to_primitive_spec() -> bool; + + spec fn spec_to_int(&self) -> Option; + + fn to_isize(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(isize::MIN <= n <= isize::MAX), + }, + default_ensures + true, + ; + + fn to_i8(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(i8::MIN <= n <= i8::MAX), + }, + default_ensures + true, + ; + + fn to_i16(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(i16::MIN <= n <= i16::MAX), + }, + default_ensures + true, + ; + + fn to_i32(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(i32::MIN <= n <= i32::MAX), + }, + default_ensures + true, + ; + + fn to_i64(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(i64::MIN <= n <= i64::MAX), + }, + ; + + fn to_i128(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(i128::MIN <= n <= i128::MAX), + }, + default_ensures + true, + ; + + fn to_usize(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(usize::MIN <= n <= usize::MAX), + }, + default_ensures + true, + ; + + fn to_u8(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(u8::MIN <= n <= u8::MAX), + }, + default_ensures + true, + ; + + fn to_u16(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(u16::MIN <= n <= u16::MAX), + }, + default_ensures + true, + ; + + fn to_u32(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(u32::MIN <= n <= u32::MAX), + }, + default_ensures + true, + ; + + fn to_u64(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(u64::MIN <= n <= u64::MAX), + }, + ; + + fn to_u128(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> + match (self.spec_to_int(), res) { + (None, None) => true, + (None, Some(_)) => false, + (Some(n1), Some(n2)) => n1 == n2, + (Some(n), None) => !(u128::MIN <= n <= u128::MAX), + }, + default_ensures + true, + ; + + spec fn spec_to_f32(&self) -> Option; + + fn to_f32(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> res == self.spec_to_f32(), + default_ensures + true, + ; + + spec fn spec_to_f64(&self) -> Option; + + fn to_f64(&self) -> (res: Option) + ensures + Self::obeys_to_primitive_spec() ==> res == self.spec_to_f64(), + default_ensures + true, + ; +} + +impl ToPrimitiveSpecImpl for num_bigint::BigInt +{ + open spec fn obeys_to_primitive_spec() -> bool + { + true + } + + open spec fn spec_to_int(&self) -> Option + { + Some(self@) + } + + uninterp spec fn spec_to_f32(&self) -> Option; + + uninterp spec fn spec_to_f64(&self) -> Option; +} + +// These are the methods of ToPrimitive that BigInt implements because there is no default in ToPrimitive +pub assume_specification[ ::to_i64 ](x: &BigInt) -> (res: Option); +pub assume_specification[ ::to_u64 ](x: &BigInt) -> (res: Option); + +// These are the methods of ToPrimitive that BigInt overrides the defaults for because they'd otherwise be wrong +pub assume_specification[ ::to_i128 ](x: &BigInt) -> (res: Option); +pub assume_specification[ ::to_u128 ](x: &BigInt) -> (res: Option); +pub assume_specification[ ::to_f32 ](x: &BigInt) -> (res: Option); +pub assume_specification[ ::to_f64 ](x: &BigInt) -> (res: Option); + +} // end verus! hidden by cfg(verus_keep_ghost) diff --git a/src/verusspec/mod.rs b/src/verusspec/mod.rs index 3062cb4..6319b84 100644 --- a/src/verusspec/mod.rs +++ b/src/verusspec/mod.rs @@ -1,4 +1,3 @@ -#[cfg(verus_keep_ghost)] pub(crate) mod bigint; pub(crate) mod float; pub(crate) mod utils;