Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 744fcca

Browse files
committedFeb 24, 2025·
Wrap template and opaque types in a marker.
As standard, bindgen treats C++ pointers and references the same in its output. Downstream postprocessors might want to treat these things differently; for example to use a CppRef<T> type for C++ references to encode the fact that they're not (usually) null. This PR emits references wrapped in a newtype wrapper called bindgen_marker_Reference<T> This behavior is enabled by an option flag; it isn't default. This type isn't actually defined in bindgen; users are expected to define or replace this during postprocessing (e.g. by using syn to reinterpret bindgen's output, or perhaps there are ways to make this useful using --raw-line or other means to inject a transparent newtype wrapper). The same approach is taken to types which bindgen chooses to make opaque. This is done in various circumstances but the most common case is for non-type template parameters. Alternative designs considered: * Apply an attribute to the function taking or returning such a param, e.g. #[bindgen_marker_takes_reference_arg(1)] fn takes_reference(a: u32, b: *const Foo) This avoids the marker type, but the problem here is a more invasive change to bindgen because type information can no longer be contained in a syn::Type; type metadata needs to be communicated around inside bindgen. * Make a ParseCallbacks call each time a type is opaque or a reference. This would work for standalone opaque types, e.g. pub struct Bar { pub _bindgen_opaque_blob: u8 } but the main case where we need these is where bindgen is using an opaque or reference type in the signature of some function, and there's no real opportunity to describe this in any kind of callback, since the callback would have to describe where exactly in the function signature the opaque or reference type has been used (and then we run into the same problems of passing this metadata around in the innards of bindgen). In order to maintain the current simple design where any C++ type is represented as a simple syn::Type, the best approach seems to be to do this wrapping in a fake marker type. Another design decision here was to represent an RValue reference as: TypeKind::Reference(_, ReferenceKind::RValue) rather than a new variant: TypeKind::RValueReference(_) In the majority of cases references are treated the same way whether they're rvalue or lvalue, so this was less invasive, but either is fine. Part of google/autocxx#124
1 parent 20aa65a commit 744fcca

File tree

10 files changed

+202
-10
lines changed

10 files changed

+202
-10
lines changed
 

‎bindgen-tests/tests/expectations/tests/opaque_newtype_wrapper.rs

+17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎bindgen-tests/tests/expectations/tests/reference_newtype_wrapper.rs

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// bindgen-flags: --use-opaque-newtype-wrapper --raw-line '#[repr(transparent)] pub struct bindgen_marker_Opaque<T: ?Sized>(T);' -- -x c++ -std=c++14
2+
3+
class Bar {};
4+
5+
template <int N>
6+
class Foo {
7+
public:
8+
int a[N];
9+
};
10+
11+
// Non-type template parameters are one of the cases where bindgen is
12+
// forced to generate an opaque type. Ensure we spot that and annotate
13+
// it.
14+
void take_foo(Foo<3> foo);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// bindgen-flags: --use-reference-newtype-wrapper --raw-line '#[repr(transparent)] pub struct bindgen_marker_Reference<T: ?Sized>(*const T);' -- -x c++ -std=c++14
2+
3+
const int& receive_reference(const int& input) {
4+
return input;
5+
}
6+
int& receive_mut_reference(int& input) {
7+
return input;
8+
}
9+
void receive_rvalue_reference(int&& input) {
10+
}

‎bindgen/codegen/helpers.rs

+34
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use proc_macro2::{Ident, Span};
44

55
use crate::ir::context::BindgenContext;
66
use crate::ir::layout::Layout;
7+
use crate::ir::ty::ReferenceKind;
78

89
pub(crate) mod attributes {
910
use proc_macro2::{Ident, Span, TokenStream};
@@ -84,6 +85,19 @@ pub(crate) fn blob(
8485
ctx: &BindgenContext,
8586
layout: Layout,
8687
ffi_safe: bool,
88+
) -> syn::Type {
89+
let inner_blob = blob_inner(ctx, layout, ffi_safe);
90+
if ctx.options().use_opaque_newtype_wrapper {
91+
syn::parse_quote! { __bindgen_marker_Opaque < #inner_blob > }
92+
} else {
93+
inner_blob
94+
}
95+
}
96+
97+
pub(crate) fn blob_inner(
98+
ctx: &BindgenContext,
99+
layout: Layout,
100+
ffi_safe: bool,
87101
) -> syn::Type {
88102
let opaque = layout.opaque();
89103

@@ -393,3 +407,23 @@ pub(crate) mod ast_ty {
393407
.collect()
394408
}
395409
}
410+
411+
pub(crate) fn reference(
412+
ty_ptr: syn::TypePtr,
413+
kind: ReferenceKind,
414+
ctx: &BindgenContext,
415+
) -> syn::Type {
416+
let ptr = syn::Type::Ptr(ty_ptr);
417+
if ctx.options().use_reference_newtype_wrapper {
418+
match kind {
419+
ReferenceKind::LValue => {
420+
syn::parse_quote! { __bindgen_marker_Reference < #ptr >}
421+
}
422+
ReferenceKind::RValue => {
423+
syn::parse_quote! { __bindgen_marker_RValueReference < #ptr >}
424+
}
425+
}
426+
} else {
427+
ptr
428+
}
429+
}

‎bindgen/codegen/mod.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -2292,7 +2292,13 @@ impl CodeGenerator for CompInfo {
22922292

22932293
if has_address {
22942294
let layout = Layout::new(1, 1);
2295-
let ty = helpers::blob(ctx, Layout::new(1, 1), false);
2295+
// Normally for opaque data we use helpers::blob,
2296+
// which may wrap it in __bindgen_marker_Opaque<T>.
2297+
// Downstream tools may then use this as a sign that
2298+
// the type is complex or can't be stack-allocated,
2299+
// etc. But in this case this one-byte field is harmless
2300+
// so we'll just represent it without that extra marker.
2301+
let ty = helpers::blob_inner(ctx, Layout::new(1, 1), false);
22962302
struct_layout.saw_field_with_layout(
22972303
"_address",
22982304
layout,
@@ -4358,7 +4364,7 @@ impl TryToRustTy for Type {
43584364
utils::build_path(item, ctx)
43594365
}
43604366
TypeKind::Opaque => self.try_to_opaque(ctx, item),
4361-
TypeKind::Pointer(inner) | TypeKind::Reference(inner) => {
4367+
TypeKind::Pointer(inner) | TypeKind::Reference(inner, _) => {
43624368
// Check that this type has the same size as the target's pointer type.
43634369
let size = self.get_layout(ctx, item).size;
43644370
if size != ctx.target_pointer_size() {
@@ -4391,7 +4397,23 @@ impl TryToRustTy for Type {
43914397
{
43924398
Ok(ty)
43934399
} else {
4394-
Ok(ty.to_ptr(is_const))
4400+
let ty_ptr = ty.to_ptr(is_const);
4401+
Ok(if let syn::Type::Ptr(ty_ptr_inside) = &ty_ptr {
4402+
// It always should be Type::Ptr, but just in case
4403+
if let TypeKind::Reference(_, reference_kind) =
4404+
self.kind()
4405+
{
4406+
helpers::reference(
4407+
ty_ptr_inside.clone(),
4408+
*reference_kind,
4409+
ctx,
4410+
)
4411+
} else {
4412+
ty_ptr
4413+
}
4414+
} else {
4415+
ty_ptr
4416+
})
43954417
}
43964418
}
43974419
TypeKind::TypeParam => {

‎bindgen/ir/analysis/has_vtable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ impl<'ctx> MonotoneFramework for HasVtableAnalysis<'ctx> {
159159
TypeKind::TemplateAlias(t, _) |
160160
TypeKind::Alias(t) |
161161
TypeKind::ResolvedTypeRef(t) |
162-
TypeKind::Reference(t) => {
162+
TypeKind::Reference(t, _) => {
163163
trace!(
164164
" aliases and references forward to their inner type"
165165
);

‎bindgen/ir/ty.rs

+27-6
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,9 @@ impl Type {
259259
) -> Option<Cow<'a, str>> {
260260
let name_info = match *self.kind() {
261261
TypeKind::Pointer(inner) => Some((inner, Cow::Borrowed("ptr"))),
262-
TypeKind::Reference(inner) => Some((inner, Cow::Borrowed("ref"))),
262+
TypeKind::Reference(inner, _) => {
263+
Some((inner, Cow::Borrowed("ref")))
264+
}
263265
TypeKind::Array(inner, length) => {
264266
Some((inner, format!("array{length}").into()))
265267
}
@@ -544,7 +546,7 @@ impl TemplateParameters for TypeKind {
544546
TypeKind::Enum(_) |
545547
TypeKind::Pointer(_) |
546548
TypeKind::BlockPointer(_) |
547-
TypeKind::Reference(_) |
549+
TypeKind::Reference(..) |
548550
TypeKind::UnresolvedTypeRef(..) |
549551
TypeKind::TypeParam |
550552
TypeKind::Alias(_) |
@@ -570,6 +572,15 @@ pub(crate) enum FloatKind {
570572
Float128,
571573
}
572574

575+
/// Different kinds of C++ reference
576+
#[derive(Debug, Clone, Copy)]
577+
pub(crate) enum ReferenceKind {
578+
/// C++ lvalue reference
579+
LValue,
580+
/// C++ rvalue reference
581+
RValue,
582+
}
583+
573584
/// The different kinds of types that we can parse.
574585
#[derive(Debug)]
575586
pub(crate) enum TypeKind {
@@ -624,7 +635,8 @@ pub(crate) enum TypeKind {
624635
BlockPointer(TypeId),
625636

626637
/// A reference to a type, as in: int& `foo()`.
627-
Reference(TypeId),
638+
/// The bool represents whether it's rvalue.
639+
Reference(TypeId, ReferenceKind),
628640

629641
/// An instantiation of an abstract template definition with a set of
630642
/// concrete template arguments.
@@ -1021,14 +1033,23 @@ impl Type {
10211033
}
10221034
// XXX: RValueReference is most likely wrong, but I don't think we
10231035
// can even add bindings for that, so huh.
1024-
CXType_RValueReference | CXType_LValueReference => {
1036+
CXType_LValueReference => {
1037+
let inner = Item::from_ty_or_ref(
1038+
ty.pointee_type().unwrap(),
1039+
location,
1040+
None,
1041+
ctx,
1042+
);
1043+
TypeKind::Reference(inner, ReferenceKind::LValue)
1044+
}
1045+
CXType_RValueReference => {
10251046
let inner = Item::from_ty_or_ref(
10261047
ty.pointee_type().unwrap(),
10271048
location,
10281049
None,
10291050
ctx,
10301051
);
1031-
TypeKind::Reference(inner)
1052+
TypeKind::Reference(inner, ReferenceKind::RValue)
10321053
}
10331054
// XXX DependentSizedArray is wrong
10341055
CXType_VariableArray | CXType_DependentSizedArray => {
@@ -1205,7 +1226,7 @@ impl Trace for Type {
12051226
}
12061227
match *self.kind() {
12071228
TypeKind::Pointer(inner) |
1208-
TypeKind::Reference(inner) |
1229+
TypeKind::Reference(inner, _) |
12091230
TypeKind::Array(inner, _) |
12101231
TypeKind::Vector(inner, _) |
12111232
TypeKind::BlockPointer(inner) |

‎bindgen/options/cli.rs

+10
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,12 @@ struct BindgenCommand {
444444
/// Always be specific about the 'receiver' of a virtual function.
445445
#[arg(long)]
446446
use_specific_virtual_function_receiver: bool,
447+
/// Use a newtype wrapper around opaque types.
448+
#[arg(long)]
449+
use_opaque_newtype_wrapper: bool,
450+
/// Use a newtype wrapper around C++ references.
451+
#[arg(long)]
452+
use_reference_newtype_wrapper: bool,
447453
/// Use distinct char16_t
448454
#[arg(long)]
449455
use_distinct_char16_t: bool,
@@ -636,6 +642,8 @@ where
636642
c_naming,
637643
explicit_padding,
638644
use_specific_virtual_function_receiver,
645+
use_opaque_newtype_wrapper,
646+
use_reference_newtype_wrapper,
639647
use_distinct_char16_t,
640648
vtable_generation,
641649
sort_semantically,
@@ -935,6 +943,8 @@ where
935943
c_naming,
936944
explicit_padding,
937945
use_specific_virtual_function_receiver,
946+
use_opaque_newtype_wrapper,
947+
use_reference_newtype_wrapper,
938948
use_distinct_char16_t,
939949
vtable_generation,
940950
sort_semantically,

‎bindgen/options/mod.rs

+43
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,49 @@ options! {
168168
as_args: "--use-specific-virtual-function-receiver",
169169
},
170170

171+
/// Use a newtype wrapper to clearly denote "opaque" types; that is,
172+
/// types where bindgen has generated a type matching the size and
173+
/// alignment of the C++ type but without any knowledge of what's
174+
/// inside it. The newtype wrapper will be a fake type called
175+
/// `bindgen_marker_Opaque`. It's assumed that you will replace this with some
176+
/// real sensible newtype wrapper of your own, either by post-processing
177+
/// the output of bindgen, or by using a `use` statemet injected using
178+
/// `--module-raw-lines` or similar.
179+
use_opaque_newtype_wrapper: bool {
180+
methods: {
181+
/// If this is true, wrap opaque types in a fake newtype
182+
/// wrapper which post-processors can replace with something
183+
/// more sensible.
184+
pub fn use_opaque_newtype_wrapper(mut self, doit: bool) -> Builder {
185+
self.options.use_opaque_newtype_wrapper = doit;
186+
self
187+
}
188+
},
189+
as_args: "--use-opaque-newtype-wrapper",
190+
},
191+
192+
/// Use a newtype wrapper to clearly denote C++ reference types.
193+
/// These are always generated as raw Rust pointers, and so otherwise
194+
/// there's no way to distinguish references from pointers.
195+
/// The newtype wrapper will be a fake type either called
196+
/// `bindgen_marker_Reference` or `bindgen_marker_RVAlueReference`.
197+
/// It's assumed that you will replace this with some
198+
/// real sensible newtype wrapper of your own, either by post-processing
199+
/// the output of bindgen, or by using a `use` statemet injected using
200+
/// `--module-raw-lines` or similar.
201+
use_reference_newtype_wrapper: bool {
202+
methods: {
203+
/// If this is true, wrap C++ references in a fake newtype
204+
/// wrapper which post-processors can replace with something
205+
/// more sensible.
206+
pub fn use_reference_newtype_wrapper(mut self, doit: bool) -> Builder {
207+
self.options.use_reference_newtype_wrapper = doit;
208+
self
209+
}
210+
},
211+
as_args: "--use-reference-newtype-wrapper",
212+
},
213+
171214
/// Whether we should distinguish between C++'s 'char16_t' and 'u16'.
172215
/// The C++ type `char16_t` is its own special type; it's not a typedef
173216
/// of some other integer (this differs from C).

0 commit comments

Comments
 (0)
Please sign in to comment.