| //@run-native |
| |
| 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() { |
| 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() { |
| 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(); |
| } |