blob: a708cedee75dd0daa4db465b0bc58b7715cf1bbb [file] [edit]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#![deny(improper_ctypes_definitions)]
use core::mem::{align_of, size_of, transmute};
use cref::{CMut, CMutTo, CRef, CRefTo};
use googletest::gtest;
#[gtest]
fn test_basic_usage() {
fn accept_some_args<T>(_: CRef<'_, T>, _: CMut<'_, T>) {}
let mut x = 0;
let cmut = CMut::from_ref(&mut x);
accept_some_args(CMut::into_const(cmut), cmut);
}
#[gtest]
fn test_macro_arguments() {
fn accept_some_args<'a, 'b>(a: CRefTo!('a, i32), b: CMutTo!('b, i32)) {
let _: CRef<'_, i32> = cref::CRefLike::into_cref(a);
let _: CMut<'_, i32> = cref::CMutLike::into_cmut(b);
}
let x = 0;
let mut y = 0;
accept_some_args(&x, &mut y);
}
// `CRef` and `CMut` have the layout of a thin pointer, and so does `Option` of each.
const _: () = {
assert!(size_of::<CRef<'static, i32>>() == size_of::<*const i32>());
assert!(align_of::<CRef<'static, i32>>() == align_of::<*const i32>());
assert!(size_of::<Option<CRef<'static, i32>>>() == size_of::<*const i32>());
assert!(size_of::<CMut<'static, i32>>() == size_of::<*mut i32>());
assert!(align_of::<CMut<'static, i32>>() == align_of::<*mut i32>());
assert!(size_of::<Option<CMut<'static, i32>>>() == size_of::<*mut i32>());
};
/// Fails to compile under `improper_ctypes_definitions` if `CRef` or `CMut` stops
/// being FFI-safe as a nullable pointer.
extern "C" fn ffi_signature(p: Option<CMut<'_, i32>>) -> Option<CRef<'_, i32>> {
p.map(CMut::into_const)
}
#[gtest]
fn test_option_none_is_all_zero_bytes() {
const N: usize = size_of::<Option<CRef<'static, i32>>>();
// SAFETY: The `std::option` docs guarantee that transmuting between
// `[0u8; size_of::<T>()]` and `Option::<T>::None` is sound, in both directions,
// for a `#[repr(transparent)]` struct around `NonNull<U>` with `U: Sized`.
// `CRef<'static, i32>` is such a struct over `NonNull<i32>`, and `N` is its size.
let none: Option<CRef<'static, i32>> = unsafe { transmute([0u8; N]) };
assert!(none.is_none());
// SAFETY: The same guarantee, for `CMut<'static, i32>`, reverse direction.
let bytes: [u8; N] = unsafe { transmute(None::<CMut<'static, i32>>) };
assert_eq!(bytes, [0u8; N]);
}
#[gtest]
fn test_ffi_signature_round_trip() {
let mut x = 7;
let px: *const i32 = &raw const x;
let r = ffi_signature(Some(CMut::from_ref(&mut x)));
assert_eq!(CRef::as_ptr(r.unwrap()), px);
assert!(ffi_signature(None).is_none());
}