diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index 2cae4d28f6..b1718002c5 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -3084,6 +3084,11 @@ class TranslateConsumer : public clang::ASTConsumer { cbor_encoder_close_container(&outer, &array); // 3. Encode all of the visited file names + // A file containing only comments has no AST nodes to register its + // file ID. Register it before emitting the table, since encoding + // comments below must not introduce file IDs missing from it. + const SourceManager& sourceMgr = Context.getSourceManager(); + visitor.getExporterFileId(sourceMgr.getMainFileID(), false); auto files = visitor.getFiles(); cbor_encoder_create_array(&outer, &array, files.size()); for (auto const &file : files) { @@ -3107,7 +3112,6 @@ class TranslateConsumer : public clang::ASTConsumer { // // Getting all comments requires -fparse-all-comments (see // augment_argv())! - const SourceManager& sourceMgr = Context.getSourceManager(); #if CLANG_VERSION_MAJOR < 10 auto comments = Context.getRawCommentList().getComments(); cbor_encoder_create_array(&outer, &array, comments.size()); diff --git a/c2rust-transpile/src/translator/atomics.rs b/c2rust-transpile/src/translator/atomics.rs index d5184b17aa..f544ea6469 100644 --- a/c2rust-transpile/src/translator/atomics.rs +++ b/c2rust-transpile/src/translator/atomics.rs @@ -34,7 +34,67 @@ pub(crate) fn order_ty_name(order: Ordering) -> &'static str { } } +// Rust represents nullable C function pointers as Option, which atomic +// intrinsics reject. Both AtomicExpr and legacy builtin calls must operate on +// the raw pointer representation, restoring the function pointer on results. +pub(crate) struct AtomicValue { + function_pointer: Option>, +} + +impl AtomicValue { + fn raw_pointer_type() -> Box { + mk().mutbl().ptr_ty(mk().tuple_ty(vec![])) + } + + pub(crate) fn storage(&self, ptr: Box) -> Box { + if self.function_pointer.is_some() { + mk().cast_expr(ptr, mk().mutbl().ptr_ty(Self::raw_pointer_type())) + } else { + ptr + } + } + + pub(crate) fn lower(&self, val: Box) -> Box { + match &self.function_pointer { + Some(ty) => transmute_expr(ty.clone(), Self::raw_pointer_type(), val), + None => val, + } + } + + pub(crate) fn restore(&self, val: Box) -> Box { + match &self.function_pointer { + Some(ty) => transmute_expr(Self::raw_pointer_type(), ty.clone(), val), + None => val, + } + } + + pub(crate) fn zero(&self) -> Box { + if self.function_pointer.is_some() { + mk().call_expr(mk().abs_path_expr(vec!["core", "ptr", "null_mut"]), vec![]) + } else { + mk().lit_expr(mk().int_lit(0, "")) + } + } +} + impl<'c> Translation<'c> { + pub(crate) fn atomic_value(&self, ptr_id: CExprId) -> TranslationResult { + let ptr_ty = self.ast_context[ptr_id].kind.get_qual_type().unwrap(); + let mut value_ty = self + .ast_context + .get_pointee_qual_type(ptr_ty.ctype) + .unwrap(); + if let CTypeKind::Atomic(inner) = self.ast_context.resolve_type(value_ty.ctype).kind { + value_ty = inner; + } + let function_pointer = if self.ast_context.is_function_pointer(value_ty.ctype) { + Some(self.convert_type(value_ty.ctype)?) + } else { + None + }; + Ok(AtomicValue { function_pointer }) + } + fn atomic_intrinsic_expr_edition_2021( &self, base_name: &str, @@ -155,6 +215,13 @@ impl<'c> Translation<'c> { .transpose()?; let weak = weak_id.and_then(|x| self.convert_constant_bool(x)); + let value = self.atomic_value(ptr_id)?; + let ptr = if name != "__c11_atomic_init" { + ptr.map(|ptr| value.storage(ptr)) + } else { + ptr + }; + fn static_order(order: Option) -> T { order.unwrap_or_else(|| { // We have to select which intrinsic to use at runtime @@ -167,7 +234,7 @@ impl<'c> Translation<'c> { let order = static_order(order); let atomic_load = self.atomic_intrinsic_expr("load", &[order]); - let call = mk().call_expr(atomic_load, vec![ptr]); + let call = value.restore(mk().call_expr(atomic_load, vec![ptr])); if name == "__atomic_load" { let ret = val1.expect("__atomic_load should have a ret argument"); Ok(ret.and_then(|ret| { @@ -200,7 +267,7 @@ impl<'c> Translation<'c> { } else { val }; - let call = mk().call_expr(atomic_store, vec![ptr, val]); + let call = mk().call_expr(atomic_store, vec![ptr, value.lower(val)]); self.convert_side_effects_expr( ctx, WithStmts::new_val(call), @@ -233,7 +300,7 @@ impl<'c> Translation<'c> { } else { val }; - let call = mk().call_expr(fn_path, vec![ptr, val]); + let call = value.restore(mk().call_expr(fn_path, vec![ptr, value.lower(val)])); if name == "__atomic_exchange" { // LLVM stores the ret pointer in the order_fail slot Ok(order_fail_id @@ -315,8 +382,10 @@ impl<'c> Translation<'c> { let atomic_cxchg = self.atomic_intrinsic_cxchg_expr(weak, order, order_fail); - let call = - mk().call_expr(atomic_cxchg, vec![ptr, expected.clone(), desired]); + let call = mk().call_expr( + atomic_cxchg, + vec![ptr, value.lower(expected.clone()), value.lower(desired)], + ); let res_name = self .renamer .borrow_mut() @@ -328,7 +397,7 @@ impl<'c> Translation<'c> { ))); let assignment = mk().semi_stmt(mk().assign_expr( expected, - mk().anon_field_expr(mk().ident_expr(&res_name), 0), + value.restore(mk().anon_field_expr(mk().ident_expr(&res_name), 0)), )); let return_value = mk().anon_field_expr(mk().ident_expr(&res_name), 1); Ok(self.convert_side_effects_expr( @@ -366,6 +435,7 @@ impl<'c> Translation<'c> { weak: bool, order_succ: Ordering, order_fail: Ordering, + ptr_id: CExprId, dst: Box, old_val: Box, src_val: Box, @@ -373,9 +443,22 @@ impl<'c> Translation<'c> { ) -> TranslationResult>> { // Emit `atomic_cxchg(a0, a1, a2).idx` let atomic_cxchg = self.atomic_intrinsic_cxchg_expr(weak, order_succ, order_fail); - let call = mk().call_expr(atomic_cxchg, vec![dst, old_val, src_val]); + let value = self.atomic_value(ptr_id)?; + let call = mk().call_expr( + atomic_cxchg, + vec![ + value.storage(dst), + value.lower(old_val), + value.lower(src_val), + ], + ); let field_idx = if returns_val { 0 } else { 1 }; let call_expr = mk().anon_field_expr(call, field_idx); + let call_expr = if returns_val { + value.restore(call_expr) + } else { + call_expr + }; Ok(self.convert_side_effects_expr( ctx, WithStmts::new_val(call_expr), diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 765b4be1c3..e8a5ddae38 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -508,6 +508,7 @@ impl<'c> Translation<'c> { false, SeqCst, SeqCst, + args[0], arg0, arg1, arg2, @@ -570,10 +571,13 @@ impl<'c> Translation<'c> { | "__sync_lock_test_and_set_16" => { // Emit `atomic_xchg_acquire(arg0, arg1)` let atomic_func = self.atomic_intrinsic_expr("xchg", &[Acquire]); + let value = self.atomic_value(args[0])?; let arg0 = self.convert_expr(ctx.used(), args[0], None)?; let arg1 = self.convert_expr(ctx.used(), args[1], None)?; Ok(arg0.zip(arg1).and_then(|(arg0, arg1)| { - let call_expr = mk().call_expr(atomic_func, vec![arg0, arg1]); + let call_expr = value.restore( + mk().call_expr(atomic_func, vec![value.storage(arg0), value.lower(arg1)]), + ); self.convert_side_effects_expr( ctx, WithStmts::new_val(call_expr), @@ -589,10 +593,11 @@ impl<'c> Translation<'c> { | "__sync_lock_release_16" => { // Emit `atomic_store_release(arg0, 0)` let atomic_func = self.atomic_intrinsic_expr("store", &[Release]); + let value = self.atomic_value(args[0])?; let arg0 = self.convert_expr(ctx.used(), args[0], None)?; Ok(arg0.and_then(|arg0| { - let zero = mk().lit_expr(mk().int_lit(0, "")); - let call_expr = mk().call_expr(atomic_func, vec![arg0, zero]); + let call_expr = + mk().call_expr(atomic_func, vec![value.storage(arg0), value.zero()]); self.convert_side_effects_expr( ctx, WithStmts::new_val(call_expr), diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 4308e61bf5..5a0a6450db 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4468,6 +4468,8 @@ impl<'c> Translation<'c> { mk().lit_expr(mk().float_unsuffixed_lit("0.")), )), } + } else if let &CTypeKind::Atomic(inner) = resolved_ty { + self.implicit_default_expr(ctx, inner.ctype) } else if let &CTypeKind::Pointer(_) = resolved_ty { self.null_ptr(resolved_ty_id).map(WithStmts::new_val) } else if let &CTypeKind::ConstantArray(elt, sz) = resolved_ty { diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 9647413fcd..65172c32ed 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -593,14 +593,31 @@ impl<'c> Translation<'c> { Mutability::Mutable => "with_exposed_provenance_mut", }, }; - let pointee_type_rs = self.convert_pointee_type(pointee_type_id.ctype)?; + // Extern types are unsized, but the exposed-provenance constructors + // require Sized pointees. Construct a c_void pointer first and cast + // it to the opaque pointer type, which is still a thin pointer. + let is_opaque = self + .ast_context + .is_forward_declared_type(pointee_type_id.ctype); + let pointee_type_rs = if is_opaque { + mk().abs_path_ty(vec!["core", "ffi", "c_void"]) + } else { + self.convert_pointee_type(pointee_type_id.ctype)? + }; let type_args = mk().angle_bracketed_args(vec![pointee_type_rs]); let fn_expr = mk().abs_path_expr(vec![ mk().path_segment("core"), mk().path_segment("ptr"), mk().path_segment_with_args(fn_name, type_args), ]); - let val = val.map(|val| mk().call_expr(fn_expr, vec![val])); + let val = val.map(|val| { + let ptr = mk().call_expr(fn_expr, vec![val]); + if is_opaque { + mk().cast_expr(ptr, target_ty) + } else { + ptr + } + }); Ok(val) } diff --git a/c2rust-transpile/src/translator/variadic.rs b/c2rust-transpile/src/translator/variadic.rs index a4824b2348..bacdf9f362 100644 --- a/c2rust-transpile/src/translator/variadic.rs +++ b/c2rust-transpile/src/translator/variadic.rs @@ -184,7 +184,7 @@ impl<'c> Translation<'c> { enum VaArgCastKind { Cast(Box), Enum(CDeclId), - Transmute, + Transmute(Box), } let mut arg_ty: Option> = None; @@ -217,7 +217,7 @@ impl<'c> Translation<'c> { is_variadic, )?; - cast_kind = Some(VaArgCastKind::Transmute); + cast_kind = Some(VaArgCastKind::Transmute(self.convert_type(ty.ctype)?)); arg_ty = Some(mk().set_mutbl(p.mutability()).ptr_ty(fn_ty)); } else if self.ast_context.is_forward_declared_type(p.ctype) { cast_kind = Some(VaArgCastKind::Cast(self.convert_type(ty.ctype).unwrap())); @@ -250,8 +250,16 @@ impl<'c> Translation<'c> { VaArgCastKind::Enum(enum_id) => { self.enum_constructor_expr(enum_id, val, false) } - VaArgCastKind::Transmute => { - transmute_expr(mk().infer_ty(), mk().infer_ty(), val) + VaArgCastKind::Transmute(ty) => { + // An enclosing cast or field access may prevent + // inference of the transmute's destination type. + // For `callbacks.fn = va_arg(arg, char_to_int_fp)`, emit + // `transmute::<_, char_to_int_fp>(arg.arg::<*mut Fn>()) + // as Option`, where Fn abbreviates the underlying + // `unsafe extern "C" fn(c_char) -> c_int` signature. + // With `transmute::<_, _>(...)`, inference fails: + // the `as` cast does not determine its input type. + transmute_expr(mk().infer_ty(), ty, val) } }; } diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index 8fde688bd5..fb47993bf5 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -312,6 +312,11 @@ fn test_bool() { transpile("bool.c").run(); } +#[test] +fn test_comments_only() { + transpile("comments_only.c").run(); +} + #[test] fn test_compound_literals() { transpile("compound_literals.c").run(); @@ -410,6 +415,11 @@ fn test_main_fn() { transpile("main_fn.c").run(); } +#[test] +fn test_opaque_pointer_casts() { + transpile("opaque_pointer_casts.c").run(); +} + #[test] fn test_out_of_range_lit() { transpile("out_of_range_lit.c").run(); diff --git a/c2rust-transpile/tests/snapshots/comments_only.c b/c2rust-transpile/tests/snapshots/comments_only.c new file mode 100644 index 0000000000..34fe9108ea --- /dev/null +++ b/c2rust-transpile/tests/snapshots/comments_only.c @@ -0,0 +1,7 @@ +/* A translation unit may contain comments without any declarations. */ + +#ifdef DISABLED_FEATURE +int feature(void) { return 42; } +#endif + +/* Keep multiple comments to exercise source location sorting. */ diff --git a/c2rust-transpile/tests/snapshots/opaque_pointer_casts.c b/c2rust-transpile/tests/snapshots/opaque_pointer_casts.c new file mode 100644 index 0000000000..fc0c19bec2 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/opaque_pointer_casts.c @@ -0,0 +1,11 @@ +struct Opaque; +union OpaqueUnion; +typedef struct Opaque Opaque; + +Opaque *sentinel(void) { return (Opaque *)8; } +const Opaque *const_sentinel(void) { return (const Opaque *)8; } +union OpaqueUnion *union_sentinel(void) { return (union OpaqueUnion *)8; } +Opaque *from_address(unsigned long address) { return (Opaque *)address; } + +static const Opaque *static_sentinel = (const Opaque *)8; +const Opaque *get_static_sentinel(void) { return static_sentinel; } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@comments_only.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@comments_only.c.2021.clang15.snap new file mode 100644 index 0000000000..252d890824 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@comments_only.c.2021.clang15.snap @@ -0,0 +1,13 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/comments_only.2021.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@comments_only.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@comments_only.c.2024.clang15.snap new file mode 100644 index 0000000000..d9aa4f6658 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@comments_only.c.2024.clang15.snap @@ -0,0 +1,14 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/comments_only.2024.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + unused_assignments, + unused_mut +)] diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2021.clang15.snap new file mode 100644 index 0000000000..7c3b5597b0 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2021.clang15.snap @@ -0,0 +1,45 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/opaque_pointer_casts.2021.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] +#![feature(extern_types, strict_provenance)] +extern "C" { + pub type Opaque; + pub type OpaqueUnion; +} +#[no_mangle] +pub unsafe extern "C" fn sentinel() -> *mut Opaque { + return ::core::ptr::from_exposed_addr_mut::<::core::ffi::c_void>( + 8 as ::core::ffi::c_int as usize, + ) as *mut Opaque; +} +#[no_mangle] +pub unsafe extern "C" fn const_sentinel() -> *const Opaque { + return ::core::ptr::from_exposed_addr::<::core::ffi::c_void>(8 as ::core::ffi::c_int as usize) + as *const Opaque; +} +#[no_mangle] +pub unsafe extern "C" fn union_sentinel() -> *mut OpaqueUnion { + return ::core::ptr::from_exposed_addr_mut::<::core::ffi::c_void>( + 8 as ::core::ffi::c_int as usize, + ) as *mut OpaqueUnion; +} +#[no_mangle] +pub unsafe extern "C" fn from_address(mut address: ::core::ffi::c_ulong) -> *mut Opaque { + return ::core::ptr::from_exposed_addr_mut::<::core::ffi::c_void>(address as usize) + as *mut Opaque; +} +static mut static_sentinel: *const Opaque = 8 as ::core::ffi::c_int as *const Opaque; +#[no_mangle] +pub unsafe extern "C" fn get_static_sentinel() -> *const Opaque { + return static_sentinel; +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2024.clang15.snap new file mode 100644 index 0000000000..38ae77ca60 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@opaque_pointer_casts.c.2024.clang15.snap @@ -0,0 +1,49 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/opaque_pointer_casts.2024.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + unused_assignments, + unused_mut +)] +#![feature(extern_types)] +unsafe extern "C" { + pub type Opaque; + pub type OpaqueUnion; +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sentinel() -> *mut Opaque { + return ::core::ptr::with_exposed_provenance_mut::<::core::ffi::c_void>( + 8 as ::core::ffi::c_int as usize, + ) as *mut Opaque; +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn const_sentinel() -> *const Opaque { + return ::core::ptr::with_exposed_provenance::<::core::ffi::c_void>( + 8 as ::core::ffi::c_int as usize, + ) as *const Opaque; +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn union_sentinel() -> *mut OpaqueUnion { + return ::core::ptr::with_exposed_provenance_mut::<::core::ffi::c_void>( + 8 as ::core::ffi::c_int as usize, + ) as *mut OpaqueUnion; +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn from_address(mut address: ::core::ffi::c_ulong) -> *mut Opaque { + return ::core::ptr::with_exposed_provenance_mut::<::core::ffi::c_void>(address as usize) + as *mut Opaque; +} +static mut static_sentinel: *const Opaque = ::core::ptr::with_exposed_provenance::< + ::core::ffi::c_void, +>(8 as ::core::ffi::c_int as usize) as *const Opaque; +#[unsafe(no_mangle)] +pub unsafe extern "C" fn get_static_sentinel() -> *const Opaque { + return static_sentinel; +} diff --git a/tests/unit/pointers/src/atomic_function_pointers.c b/tests/unit/pointers/src/atomic_function_pointers.c new file mode 100644 index 0000000000..7d2d6a5617 --- /dev/null +++ b/tests/unit/pointers/src/atomic_function_pointers.c @@ -0,0 +1,55 @@ +typedef int (*callback)(int); + +static int increment(int x) { return x + 1; } +static int decrement(int x) { return x - 1; } + +int atomic_function_pointers(void) { + callback ptr = 0; + callback expected = 0; + callback desired = increment; + callback result = 0; + + __atomic_store_n(&ptr, increment, __ATOMIC_RELEASE); + result = __atomic_load_n(&ptr, __ATOMIC_ACQUIRE); + if (!result || result(10) != 11) return 1; + result = __atomic_exchange_n(&ptr, decrement, __ATOMIC_ACQ_REL); + if (result != increment) return 2; + if (__atomic_compare_exchange_n(&ptr, &expected, increment, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return 3; + if (expected != decrement) return 4; + if (!__atomic_compare_exchange_n(&ptr, &expected, (callback)0, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return 5; + if (__atomic_load_n(&ptr, __ATOMIC_RELAXED)) return 6; + + __atomic_store(&ptr, &desired, __ATOMIC_RELEASE); + __atomic_load(&ptr, &result, __ATOMIC_ACQUIRE); + if (result != increment) return 7; + desired = decrement; + __atomic_exchange(&ptr, &desired, &result, __ATOMIC_ACQ_REL); + if (result != increment) return 8; + expected = increment; + desired = 0; + if (__atomic_compare_exchange(&ptr, &expected, &desired, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return 9; + if (expected != decrement) return 10; + if (!__atomic_compare_exchange(&ptr, &expected, &desired, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return 11; + __atomic_load(&ptr, &result, __ATOMIC_RELAXED); + if (result) return 12; + + _Atomic(callback) c11_ptr = (callback)0; + __c11_atomic_init(&c11_ptr, (callback)0); + __c11_atomic_store(&c11_ptr, increment, __ATOMIC_RELEASE); + result = __c11_atomic_load(&c11_ptr, __ATOMIC_ACQUIRE); + if (!result || result(10) != 11) return 13; + result = __c11_atomic_exchange(&c11_ptr, decrement, __ATOMIC_ACQ_REL); + if (result != increment) return 14; + expected = increment; + if (__c11_atomic_compare_exchange_strong(&c11_ptr, &expected, (callback)0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return 15; + if (expected != decrement) return 16; + if (!__c11_atomic_compare_exchange_strong(&c11_ptr, &expected, (callback)0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) return 17; + if (__c11_atomic_load(&c11_ptr, __ATOMIC_RELAXED)) return 18; + return 0; +} diff --git a/tests/unit/pointers/src/function_pointers.c b/tests/unit/pointers/src/function_pointers.c index aaac4639ad..f6da3e924b 100644 --- a/tests/unit/pointers/src/function_pointers.c +++ b/tests/unit/pointers/src/function_pointers.c @@ -26,6 +26,24 @@ int varargs_fp(const int c, ...) { return fp((char)c); } +// The typedef forces a cast on the va_arg result when assigning to a field +// spelled with the underlying function pointer type (as in SQLite's config). +int varargs_fp_field(const int c, ...) { + struct { int (*fn)(char); } callbacks; + va_list arg; + va_start(arg, c); + callbacks.fn = va_arg(arg, char_to_int_fp); + va_end(arg); + return callbacks.fn ? callbacks.fn((char)c) : -1; +} + +int varargs_fp_field_test(void) { + char_to_int_fp null_fp = 0; + return varargs_fp_field('a', intval) == 'a' && + varargs_fp_field('b', negintval) == -'b' && + varargs_fp_field('c', null_fp) == -1; +} + #endif void entry3(const unsigned sz, int buffer[const]) { diff --git a/tests/unit/pointers/src/sync_function_pointers.c b/tests/unit/pointers/src/sync_function_pointers.c new file mode 100644 index 0000000000..ff8ec57f45 --- /dev/null +++ b/tests/unit/pointers/src/sync_function_pointers.c @@ -0,0 +1,30 @@ +typedef int (*callback)(int); + +static int increment(int x) { return x + 1; } +static int decrement(int x) { return x - 1; } + +int sync_function_pointers(void) { + callback ptr = 0; + callback result = __sync_lock_test_and_set(&ptr, increment); + if (result || !ptr || ptr(10) != 11) return 1; + result = __sync_lock_test_and_set(&ptr, decrement); + if (result != increment || ptr != decrement) return 2; + + result = __sync_val_compare_and_swap(&ptr, increment, (callback)0); + if (result != decrement || ptr != decrement) return 3; + result = __sync_val_compare_and_swap(&ptr, decrement, (callback)0); + if (result != decrement || ptr) return 4; + result = __sync_val_compare_and_swap(&ptr, (callback)0, increment); + if (result || ptr != increment) return 5; + + if (__sync_bool_compare_and_swap(&ptr, decrement, (callback)0)) return 6; + if (ptr != increment) return 7; + if (!__sync_bool_compare_and_swap(&ptr, increment, (callback)0)) return 8; + if (ptr) return 9; + if (!__sync_bool_compare_and_swap(&ptr, (callback)0, decrement)) return 10; + if (!ptr || ptr(10) != 9) return 11; + + __sync_lock_release(&ptr); + if (ptr) return 12; + return 0; +} diff --git a/tests/unit/pointers/src/test_pointers.rs b/tests/unit/pointers/src/test_pointers.rs index beb74e85c6..2f05520e4f 100644 --- a/tests/unit/pointers/src/test_pointers.rs +++ b/tests/unit/pointers/src/test_pointers.rs @@ -1,4 +1,4 @@ -//! feature_c_variadic, feature_raw_ref_op, feature_strict_provenance +//! feature_c_variadic, feature_raw_ref_op, feature_strict_provenance, feature_core_intrinsics use crate::function_pointers::rust_entry3; use crate::pointer_arith::rust_entry2; @@ -10,11 +10,47 @@ use std::ffi::{c_int, c_uint}; #[link(name = "test")] extern "C" { + fn atomic_function_pointers() -> c_int; + fn sync_function_pointers() -> c_int; fn entry(_: c_uint, _: *mut c_int); fn entry2(_: c_uint, _: *mut c_int); fn entry3(_: c_uint, _: *mut c_int); + + #[cfg(not(target_arch = "aarch64"))] + fn varargs_fp_field_test() -> c_int; +} + +#[test] +pub fn test_sync_function_pointers() { + unsafe { + assert_eq!(sync_function_pointers(), 0); + assert_eq!( + crate::sync_function_pointers::rust_sync_function_pointers(), + 0 + ); + } +} + +#[test] +pub fn test_atomic_function_pointers() { + unsafe { + assert_eq!(atomic_function_pointers(), 0); + assert_eq!( + crate::atomic_function_pointers::rust_atomic_function_pointers(), + 0 + ); + } +} + +#[test] +#[cfg(not(target_arch = "aarch64"))] +pub fn test_varargs_fp_field() { + unsafe { + assert_eq!(varargs_fp_field_test(), 1); + assert_eq!(crate::function_pointers::rust_varargs_fp_field_test(), 1); + } } const BUFFER_SIZE: usize = 5;