diff --git a/bindgen-tests/tests/expectations/tests/references.rs b/bindgen-tests/tests/expectations/tests/references.rs new file mode 100644 index 0000000000..799e855793 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/references.rs @@ -0,0 +1,61 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Container { + pub normalPointer: *mut ::std::os::raw::c_int, + pub constPointer: *const ::std::os::raw::c_int, + pub normalRef: ::std::ptr::NonNull<::std::os::raw::c_int>, + pub constRef: ::std::ptr::NonNull<::std::os::raw::c_int>, + pub pointerRef: ::std::ptr::NonNull<*mut ::std::os::raw::c_int>, + pub constPointerRef: ::std::ptr::NonNull<*const ::std::os::raw::c_int>, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of Container"][::std::mem::size_of::() - 48usize]; + ["Alignment of Container"][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: Container::normalPointer", + ][::std::mem::offset_of!(Container, normalPointer) - 0usize]; + [ + "Offset of field: Container::constPointer", + ][::std::mem::offset_of!(Container, constPointer) - 8usize]; + [ + "Offset of field: Container::normalRef", + ][::std::mem::offset_of!(Container, normalRef) - 16usize]; + [ + "Offset of field: Container::constRef", + ][::std::mem::offset_of!(Container, constRef) - 24usize]; + [ + "Offset of field: Container::pointerRef", + ][::std::mem::offset_of!(Container, pointerRef) - 32usize]; + [ + "Offset of field: Container::constPointerRef", + ][::std::mem::offset_of!(Container, constPointerRef) - 40usize]; +}; +impl Default for Container { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +unsafe extern "C" { + #[link_name = "\u{1}_Z20refReturningFunctionv"] + pub fn refReturningFunction() -> ::std::ptr::NonNull<::std::os::raw::c_int>; +} +unsafe extern "C" { + #[link_name = "\u{1}_Z20functionConsumingRefRifRKi"] + pub fn functionConsumingRef( + someRef: ::std::ptr::NonNull<::std::os::raw::c_int>, + normalArgument: f32, + constRef: ::std::ptr::NonNull<::std::os::raw::c_int>, + ); +} +unsafe extern "C" { + #[link_name = "\u{1}_Z27functionConsumingPointerRefRPi"] + pub fn functionConsumingPointerRef( + pointerRef: ::std::ptr::NonNull<*mut ::std::os::raw::c_int>, + ); +} diff --git a/bindgen-tests/tests/headers/references.hpp b/bindgen-tests/tests/headers/references.hpp new file mode 100644 index 0000000000..6b56823d12 --- /dev/null +++ b/bindgen-tests/tests/headers/references.hpp @@ -0,0 +1,17 @@ +// bindgen-flags: --nonnull-references + +struct Container { + int *normalPointer; + const int *constPointer; + int &normalRef; + const int &constRef; + int *&pointerRef; + const int *&constPointerRef; +}; + +int &refReturningFunction(); + +void functionConsumingRef(int &someRef, float normalArgument, + const int &constRef); + +void functionConsumingPointerRef(int* &pointerRef); diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 585845baeb..37744a34bf 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4384,6 +4384,12 @@ impl TryToRustTy for Type { if inner_ty.canonical_type(ctx).is_function() || is_objc_pointer { Ok(ty) + } else if ctx.options().generate_cxx_nonnull_references && + matches!(self.kind(), TypeKind::Reference(_)) + { + // It's UB to pass null values in place of C++ references + let prefix = ctx.trait_prefix(); + Ok(syn::parse_quote! { ::#prefix::ptr::NonNull<#ty> }) } else { Ok(ty.to_ptr(is_const)) } diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index b60de39603..4334e9c239 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -258,6 +258,9 @@ struct BindgenCommand { /// Use extern crate instead of use for objc. #[arg(long)] objc_extern_crate: bool, + /// Use `NonNull` in place of raw pointers for C++ references. + #[arg(long)] + nonnull_references: bool, /// Generate block signatures instead of void pointers. #[arg(long)] generate_block: bool, @@ -590,6 +593,7 @@ where no_doc_comments, no_recursive_allowlist, objc_extern_crate, + nonnull_references, generate_block, generate_cstr, block_extern_crate, @@ -921,6 +925,7 @@ where no_doc_comments => |b, _| b.generate_comments(false), no_recursive_allowlist => |b, _| b.allowlist_recursively(false), objc_extern_crate, + nonnull_references => |b, _| b.generate_cxx_nonnull_references(true), generate_block, generate_cstr, block_extern_crate, diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 767be03e35..cca5982b64 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -1474,6 +1474,26 @@ options! { }, as_args: |value, args| (!value).as_args(args, "--no-doc-comments"), }, + /// Whether to generate [`NonNull`] pointers for C++ references. + /// + /// [`NonNull`]: core::ptr::NonNull + generate_cxx_nonnull_references: bool { + default: false, + methods: { + /// Generate `NonNull` pointers in place of raw pointers for C++ + /// references. + /// + /// This option is disabled by default: + /// + /// Enabling it erases information about constness in generated + /// code, and `NonNull` is more cumbersome to use than raw pointers. + pub fn generate_cxx_nonnull_references(mut self, doit: bool) -> Self { + self.options.generate_cxx_nonnull_references = doit; + self + } + }, + as_args: |value, args| value.as_args(args, "--nonnull-references"), + }, /// Whether to generate inline functions. generate_inline_functions: bool { methods: {