| //@run-native | |
| #![feature(c_variadic)] | |
| use std::ffi::{CStr, VaList, c_char, c_double, c_int, c_long}; | |
| fn ignores_arguments() { | |
| unsafe extern "C" fn variadic(_: ...) {} | |
| unsafe { variadic() }; | |
| unsafe { variadic(1, 2, 3) }; | |
| } | |
| fn echo() { | |
| unsafe extern "C" fn variadic(mut ap: ...) -> i32 { | |
| ap.next_arg() | |
| } | |
| assert_eq!(unsafe { variadic(1) }, 1); | |
| assert_eq!(unsafe { variadic(3, 2, 1) }, 3); | |
| } | |
| fn forward_by_val() { | |
| unsafe fn helper(mut ap: VaList) -> i32 { | |
| ap.next_arg() | |
| } | |
| unsafe extern "C" fn variadic(ap: ...) -> i32 { | |
| helper(ap) | |
| } | |
| assert_eq!(unsafe { variadic(1) }, 1); | |
| assert_eq!(unsafe { variadic(3, 2, 1) }, 3); | |
| } | |
| fn forward_by_ref() { | |
| unsafe fn helper(ap: &mut VaList) -> i32 { | |
| ap.next_arg() | |
| } | |
| unsafe extern "C" fn variadic(mut ap: ...) -> i32 { | |
| helper(&mut ap) | |
| } | |
| assert_eq!(unsafe { variadic(1) }, 1); | |
| assert_eq!(unsafe { variadic(3, 2, 1) }, 3); | |
| } | |
| #[allow(improper_ctypes_definitions)] | |
| fn nested() { | |
| unsafe fn helper(mut ap1: VaList, mut ap2: VaList) -> (i32, i32) { | |
| (ap1.next_arg(), ap2.next_arg()) | |
| } | |
| unsafe extern "C" fn variadic2(ap1: VaList, ap2: ...) -> (i32, i32) { | |
| helper(ap1, ap2) | |
| } | |
| unsafe extern "C" fn variadic1(ap1: ...) -> (i32, i32) { | |
| variadic2(ap1, 2, 2) | |
| } | |
| assert_eq!(unsafe { variadic1(1) }, (1, 2)); | |
| let (a, b) = unsafe { variadic1(1, 1) }; | |
| assert_eq!(a, 1); | |
| assert_eq!(b, 2); | |
| } | |
| fn various_types() { | |
| unsafe extern "C" fn check_list_2(mut ap: ...) { | |
| macro_rules! continue_if { | |
| ($cond:expr) => { | |
| if !($cond) { | |
| panic!(); | |
| } | |
| }; | |
| } | |
| unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool { | |
| match CStr::from_ptr(ptr).to_str() { | |
| Ok(cstr) => cstr == val, | |
| Err(_) => panic!(), | |
| } | |
| } | |
| continue_if!(ap.next_arg::<c_double>().floor() == 3.14f64.floor()); | |
| continue_if!(ap.next_arg::<c_long>() == 12); | |
| continue_if!(ap.next_arg::<c_int>() == 'a' as c_int); | |
| continue_if!(ap.next_arg::<c_double>().floor() == 6.18f64.floor()); | |
| continue_if!(compare_c_str(ap.next_arg::<*const c_char>(), "Hello")); | |
| continue_if!(ap.next_arg::<c_int>() == 42); | |
| continue_if!(compare_c_str(ap.next_arg::<*const c_char>(), "World")); | |
| } | |
| unsafe { | |
| check_list_2( | |
| 3.14 as c_double, | |
| 12 as c_long, | |
| b'a' as c_int, | |
| 6.28 as c_double, | |
| c"Hello".as_ptr(), | |
| 42 as c_int, | |
| c"World".as_ptr(), | |
| ); | |
| } | |
| } | |
| fn equal_up_to_free_lifetime() { | |
| // Types are considered equal up to free lifetimes: `*const &'static str` | |
| // is the same as `*const &'a str`. | |
| // Bound lifetimes (using e.g. `for<'_>`) are different. | |
| #[expect(improper_ctypes_definitions)] | |
| pub unsafe extern "C" fn foo(mut args: ...) -> &'static str { | |
| unsafe { *args.next_arg::<*const &'static str>() } | |
| } | |
| let data = String::from("abc"); | |
| let x: &str = data.as_str(); | |
| assert_eq!(unsafe { foo(&raw const x) }, "abc"); | |
| } | |
| fn clone() { | |
| if cfg!(force_intrinsic_fallback) { | |
| // Skip this test when we use the fallback bodies. The fallback body does | |
| // not hook into the Miri allocation bookkeeping for variable argument lists | |
| // and would would falsely report UB. | |
| return; | |
| } | |
| unsafe extern "C" fn clone_the_va_list(args: ...) { | |
| // The implicit `drop` will catch a `VaList` that isn't properly initialized. | |
| let _ = args.clone(); | |
| } | |
| unsafe { clone_the_va_list(1i32, 2i32) } | |
| } | |
| fn clone_and_advance() { | |
| if cfg!(force_intrinsic_fallback) { | |
| // Skip this test when we use the fallback bodies. The fallback body does | |
| // not hook into the Miri allocation bookkeeping for variable argument lists | |
| // and would would falsely report UB. | |
| return; | |
| } | |
| unsafe extern "C" fn variadic(mut a: ...) { | |
| unsafe { | |
| let mut b = a.clone(); | |
| assert_eq!(a.next_arg::<i32>(), 1i32); | |
| assert_eq!(b.next_arg::<i32>(), 1i32); | |
| assert_eq!(a.next_arg::<i32>(), 2i32); | |
| let mut c = a.clone(); | |
| let mut d = b.clone(); | |
| assert_eq!(a.next_arg::<i32>(), 3i32); | |
| assert_eq!(b.next_arg::<i32>(), 2i32); | |
| assert_eq!(c.next_arg::<i32>(), 3i32); | |
| assert_eq!(d.next_arg::<i32>(), 2i32); | |
| assert_eq!(c.next_arg::<i32>(), 4i32); | |
| assert_eq!(a.next_arg::<i32>(), 4i32); | |
| drop(c); | |
| drop(a); | |
| assert_eq!(d.next_arg::<i32>(), 3i32); | |
| assert_eq!(d.next_arg::<i32>(), 4i32); | |
| assert_eq!(b.next_arg::<i32>(), 3i32); | |
| assert_eq!(b.next_arg::<i32>(), 4i32); | |
| } | |
| } | |
| unsafe { variadic(1i32, 2i32, 3i32, 4i32) } | |
| } | |
| fn main() { | |
| ignores_arguments(); | |
| echo(); | |
| forward_by_val(); | |
| forward_by_ref(); | |
| nested(); | |
| various_types(); | |
| equal_up_to_free_lifetime(); | |
| clone(); | |
| clone_and_advance(); | |
| } |