Skip to content

Commit 346ec7b

Browse files
committed
transmute_unchecked harnesses
1 parent 5e44e6e commit 346ec7b

File tree

2 files changed

+198
-2
lines changed

2 files changed

+198
-2
lines changed

library/core/src/intrinsics.rs

+197-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
)]
6565
#![allow(missing_docs)]
6666

67-
use safety::requires;
67+
use safety::{requires,ensures};
6868
use crate::marker::{DiscriminantKind, Tuple};
6969
use crate::mem::SizedTypeProperties;
7070
use crate::{ptr, ub_checks};
@@ -3756,6 +3756,33 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
37563756
const_eval_select((ptr, align), compiletime, runtime);
37573757
}
37583758

3759+
use crate::any;
3760+
#[requires(crate::mem::size_of::<T>() >= crate::mem::size_of::<U>())] //U cannot be larger than T
3761+
#[ensures(|ret: &U| (ret as *const U as usize) % crate::mem::align_of::<U>() == 0)] //check that the output has expected alignment
3762+
pub unsafe fn transmute_unchecked_wrapper<T,U>(input: T) -> U {
3763+
transmute_unchecked(input)
3764+
}
3765+
3766+
//This requires means [output is char implies input is valid unicode value]
3767+
#[requires(type_name::<U>() != type_name::<char>() || (input <= T::from(0xD7FF) || (input >= T::from(0xE000) && input <= T::from(0x10FFFF)) ))]
3768+
#[ensures(|ret: &U| (ret as *const U as usize) % crate::mem::align_of::<U>() == 0)]
3769+
pub unsafe fn transmute_unchecked_from_u32<T,U>(input: T) -> U
3770+
where
3771+
T: crate::ops::BitAnd<Output = T> + PartialEq + From<u32> + Copy + PartialOrd,
3772+
{
3773+
transmute_unchecked(input)
3774+
}
3775+
3776+
//This requires means [output is bool implies input is 0 or 1]
3777+
#[requires(type_name::<U>() != type_name::<bool>() || (input & T::from(0xFF) == T::from(0) || input & T::from(0xFF) == T::from(1)))]
3778+
#[ensures(|ret: &U| (ret as *const U as usize) % crate::mem::align_of::<U>() == 0)]
3779+
pub unsafe fn transmute_unchecked_from_u8<T,U>(input: T) -> U
3780+
where
3781+
T: crate::ops::BitAnd<Output = T> + PartialEq + From<u8> + Copy + PartialOrd,
3782+
{
3783+
transmute_unchecked(input)
3784+
}
3785+
37593786
#[cfg(kani)]
37603787
#[unstable(feature="kani", issue="none")]
37613788
mod verify {
@@ -3788,4 +3815,173 @@ mod verify {
37883815
assert_eq!(y, old_x);
37893816
assert_eq!(x, old_y);
37903817
}
3818+
3819+
//this unexpectedly doesn't compile due to different sized inputs
3820+
//Normally, transmute_unchecked should be fine as long as output is not larger
3821+
/*#[kani::proof_for_contract(transmute_unchecked_wrapper)]
3822+
fn transmute_different_sizes() {
3823+
let large_value: u64 = kani::any();
3824+
unsafe {
3825+
let truncated_value: u32 = transmute_unchecked_wrapper(large_value);
3826+
//assert!((truncated_value as u32) == 64);
3827+
}
3828+
}*/
3829+
3830+
#[kani::proof_for_contract(transmute_unchecked_wrapper)]
3831+
fn transmute_zero_size() {
3832+
let empty_arr: [u8;0] = [];
3833+
let unit_val: () = unsafe { transmute_unchecked_wrapper(empty_arr) };
3834+
assert!(unit_val == ());
3835+
}
3836+
3837+
//this should only transmute valid values due to precondition
3838+
#[kani::proof_for_contract(transmute_unchecked_from_u32)]
3839+
fn transmute_u32_to_char() {
3840+
let num: u32 = kani::any();
3841+
let c: char = unsafe {transmute_unchecked_from_u32(num)};
3842+
assert!((c as u32 <= 0xD7FF) || (c as u32 >= 0xE000 && c as u32 <= 0x10FFFF))
3843+
}
3844+
3845+
//Note: this doesn't actually panic, because violating char's
3846+
//type invariants is not something that transmute checks for
3847+
#[kani::proof]
3848+
#[kani::should_panic]
3849+
fn transmute_invalid_u32_to_char() {
3850+
let num: u32 = kani::any();
3851+
let c: char = unsafe {transmute_unchecked_from_u32(num)};
3852+
}
3853+
3854+
//only transmutes 0 or 1 to bool due to requires clause
3855+
//so only uses valid values
3856+
#[kani::proof_for_contract(transmute_unchecked_from_u8)]
3857+
fn transmute_u8_to_bool() {
3858+
let num: u8 = kani::any();
3859+
let b: bool = unsafe {transmute_unchecked_from_u8(num)};
3860+
assert!(b == (num == 1));
3861+
}
3862+
3863+
//Note: this doesn't actually panic, because violating bool's
3864+
//type invariants is not something that transmute checks for.
3865+
#[kani::proof]
3866+
#[kani::should_panic]
3867+
fn transmute_invalid_u8_to_bool() {
3868+
let num: u8 = kani::any();
3869+
let b: bool = unsafe {transmute_unchecked_from_u8(num)};
3870+
}
3871+
3872+
#[repr(C)]
3873+
struct A {
3874+
x: u8,
3875+
y: u16,
3876+
z: u8,
3877+
}
3878+
3879+
#[repr(C)]
3880+
struct B {
3881+
x: u8,
3882+
y: u8,
3883+
z: u16,
3884+
}
3885+
3886+
#[repr(C)]
3887+
struct C {
3888+
x: u16,
3889+
y: u16,
3890+
z: u16,
3891+
}
3892+
3893+
//this doesn't compile, A and B have different sizes due to padding
3894+
/*#[kani::proof_for_contract(transmute_unchecked_wrapper)]
3895+
fn transmute_unchecked_padding() {
3896+
let a = A {x: kani::any(), y: kani::any(), z: kani::any()};
3897+
let x = a.x;
3898+
3899+
let b: B = unsafe { transmute_unchecked_wrapper(a) };
3900+
assert!(b.x == x);
3901+
}*/
3902+
3903+
#[kani::proof_for_contract(transmute_unchecked_wrapper)]
3904+
fn transmute_unchecked_padding() {
3905+
let a = A {x: kani::any(), y: kani::any(), z: kani::any()};
3906+
let x = a.x;
3907+
3908+
let c: C = unsafe { transmute_unchecked_wrapper(a) };
3909+
assert!(c.x as u8 == x);
3910+
}
3911+
3912+
#[kani::proof_for_contract(transmute_unchecked_wrapper)]
3913+
fn transmute_unchecked_2ways_i64_u64() {
3914+
let a: i64 = kani::any();
3915+
let b: u64 = unsafe { transmute_unchecked_wrapper(a) };
3916+
let c: i64 = unsafe { transmute_unchecked_wrapper(b) };
3917+
assert_eq!(a,c);
3918+
}
3919+
3920+
#[kani::proof_for_contract(transmute_unchecked_wrapper)]
3921+
fn transmute_unchecked_2ways_i32_u32() {
3922+
let a: i32 = kani::any();
3923+
let b: u32 = unsafe { transmute_unchecked_wrapper(a) };
3924+
let c: i32 = unsafe { transmute_unchecked_wrapper(b) };
3925+
assert_eq!(a,c);
3926+
}
3927+
3928+
#[kani::proof_for_contract(transmute_unchecked_wrapper)]
3929+
fn transmute_unchecked_2ways_i8_u8() {
3930+
let a: i8 = kani::any();
3931+
let b: u8 = unsafe { transmute_unchecked_wrapper(a) };
3932+
let c: i8 = unsafe { transmute_unchecked_wrapper(b) };
3933+
assert_eq!(a,c);
3934+
}
3935+
3936+
#[kani::proof]
3937+
fn test_transmute_u32_to_bytes() {
3938+
// Create a nondeterministic u32 value.
3939+
let num: u32 = kani::any();
3940+
3941+
// SAFETY: This transmute is safe because u32 and [u8; 4] have the same size.
3942+
let bytes: [u8; 4] = unsafe { transmute(num) };
3943+
3944+
// To check if transmute preserves data, we can reinterpret bytes back to u32.
3945+
let num_back: u32 = unsafe { transmute(bytes) };
3946+
3947+
// Assert that the original number and the reconstructed number are the same.
3948+
assert_eq!(num, num_back, "Transmute did not preserve the value");
3949+
}
3950+
3951+
//This doesn't compile, since transmuting values of different sizes is not allowed
3952+
/*#[kani::proof]
3953+
fn transmute_different_sizes() {
3954+
let large_value: u64 = kani::any();
3955+
unsafe {
3956+
let truncated_value: u32 = transmute(large_value);
3957+
assert!(truncated_value == (large_value as u32));
3958+
}
3959+
}*/
3960+
3961+
#[kani::proof]
3962+
fn transmute_zero_size() {
3963+
let empty_arr: [u8;0] = [];
3964+
let unit_val: () = unsafe { transmute(empty_arr) };
3965+
assert!(unit_val == ());
3966+
}
3967+
3968+
#[kani::proof]
3969+
fn transmute_different_validy_reqs() {
3970+
let num: u8 = kani::any();
3971+
if num <= 1 {
3972+
let b: bool = unsafe {transmute(num)};
3973+
assert!(b == (num == 1));
3974+
}
3975+
//What do we check if num > 1?
3976+
}
3977+
3978+
#[kani::proof]
3979+
fn test_transmute_same_size() {
3980+
let a: i32 = kani::any();
3981+
let b: u32 = unsafe { transmute(a) };
3982+
let c: i32 = unsafe { transmute(b) };
3983+
assert_eq!(a,c);
3984+
}
3985+
>>>>>>> 3148918... added transmute harnesses
3986+
37913987
}

library/stdarch

Submodule stdarch updated 66 files

0 commit comments

Comments
 (0)