Skip to content

Commit 2b6e50b

Browse files
Googlercopybara-github
authored andcommitted
Make "bridge-by-value types are not supported in struct fields" more specific
PiperOrigin-RevId: 901444701
1 parent 8c59789 commit 2b6e50b

File tree

5 files changed

+115
-1
lines changed

5 files changed

+115
-1
lines changed

docs/errors/bridge_field.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<!-- <internal link> -->
2+
3+
# Bridge types as struct fields
4+
5+
## Overview
6+
7+
Crubit does not support exposing struct or union fields with bridge types.
8+
9+
Bridge types are types that are converted at runtime between C++ and Rust
10+
(`std::optional` mapping to `Option`), but have different underlying
11+
representations. (See crubit.rs/types)
12+
13+
If a field is a bridge type, this conversion is impossible: field accesses do
14+
not run any conversion logic, and the field is required to have identical
15+
representation across both languages. As a result, the field will not have the
16+
bridge type conversion applied, but instead will be replaced by an opaque blob
17+
of bytes.
18+
19+
## Example
20+
21+
Consider the following C++ struct:
22+
23+
```c++
24+
#include <optional>
25+
#include <cstdint>
26+
27+
struct Config {
28+
std::optional<int> foo;
29+
};
30+
```
31+
32+
Crubit will treat the `foo` field of `Config` as an opaque blob of bytes that
33+
cannot be accessed directly from Rust.
34+
35+
## Workaround: Add getter and setter methods {#workaround}
36+
37+
To access or modify the field from Rust, you can add getter and setter methods.
38+
Crubit *can* generate bindings for functions that accept or return bridge types
39+
by value.
40+
41+
For example:
42+
43+
```c++
44+
#include <optional>
45+
46+
struct Config {
47+
std::optional<int> foo;
48+
49+
// Getter returning by value (mapped to Option<i32> in Rust)
50+
std::optional<int> get_foo() const { return foo; }
51+
52+
// Setter accepting by value
53+
void set_foo(std::optional<int> val) { foo = val; }
54+
};
55+
```
56+
57+
Then in Rust, you can use these methods:
58+
59+
```rust
60+
let mut config = Config::default();
61+
config.set_foo(Some(42));
62+
assert_eq!(config.get_foo(), Some(42));
63+
```
64+
65+
See crubit.rs/cpp/best_practices#bridging for more information.

rs_bindings_from_cc/generate_bindings/generate_struct_and_union.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ fn get_field_rs_type_kind_for_layout(
135135
}
136136

137137
if type_kind.is_bridge_type() {
138-
bail!("Bridge-by-value types are not supported in struct fields.")
138+
bail!("crubit.rs/errors/bridge_field: '{}' is a bridge type, but fields must be layout compatible between Rust and C++.",
139+
type_kind.display(db))
139140
}
140141

141142
for target in

rs_bindings_from_cc/test/golden/composable_bridging.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ struct
1919
// clang-format on
2020
CppStruct {};
2121

22+
struct StructWithBridgeField {
23+
CppStruct bridge_field;
24+
};
25+
2226
CppStruct ReturnCppStruct();
2327

2428
void TakeCppStruct(CppStruct);

rs_bindings_from_cc/test/golden/composable_bridging_rs_api.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,32 @@
1818
// order for the generated code to properly compile. This example just serves to
1919
// illustrate what the generated code will look like.
2020

21+
#[derive(Clone, Copy, ::ctor::MoveAndAssignViaCopy)]
22+
#[repr(C)]
23+
///CRUBIT_ANNOTATE: cpp_type=StructWithBridgeField
24+
pub struct StructWithBridgeField {
25+
/// Reason for representing this field as a blob of bytes:
26+
/// crubit.rs/errors/bridge_field: 'crate::RustStruct' is a bridge type, but fields must be layout compatible between Rust and C++.
27+
pub(crate) bridge_field: [::core::mem::MaybeUninit<u8>; 1],
28+
}
29+
impl !Send for StructWithBridgeField {}
30+
impl !Sync for StructWithBridgeField {}
31+
unsafe impl ::cxx::ExternType for StructWithBridgeField {
32+
type Id = ::cxx::type_id!("StructWithBridgeField");
33+
type Kind = ::cxx::kind::Trivial;
34+
}
35+
36+
impl Default for StructWithBridgeField {
37+
#[inline(always)]
38+
fn default() -> Self {
39+
let mut tmp = ::core::mem::MaybeUninit::<Self>::zeroed();
40+
unsafe {
41+
crate::detail::__rust_thunk___ZN21StructWithBridgeFieldC1Ev(&raw mut tmp as *mut _);
42+
tmp.assume_init()
43+
}
44+
}
45+
}
46+
2147
#[inline(always)]
2248
pub fn ReturnCppStruct() -> crate::RustStruct {
2349
unsafe {
@@ -568,6 +594,9 @@ mod detail {
568594
#[allow(unused_imports)]
569595
use super::*;
570596
unsafe extern "C" {
597+
pub(crate) unsafe fn __rust_thunk___ZN21StructWithBridgeFieldC1Ev(
598+
__this: *mut ::core::ffi::c_void,
599+
);
571600
pub(crate) unsafe fn __rust_thunk___Z15ReturnCppStructv(
572601
__return_abi_buffer: *mut ::core::ffi::c_uchar,
573602
);
@@ -617,6 +646,11 @@ mod detail {
617646
}
618647

619648
const _: () = {
649+
assert!(::core::mem::size_of::<crate::StructWithBridgeField>() == 1);
650+
assert!(::core::mem::align_of::<crate::StructWithBridgeField>() == 1);
651+
static_assertions::assert_impl_all!(crate::StructWithBridgeField: Copy,Clone);
652+
static_assertions::assert_not_impl_any!(crate::StructWithBridgeField: Drop);
653+
assert!(::core::mem::offset_of!(crate::StructWithBridgeField, bridge_field) == 0);
620654
assert!(::core::mem::size_of::<crate::Vec3>() == 12);
621655
assert!(::core::mem::align_of::<crate::Vec3>() == 4);
622656
static_assertions::assert_impl_all!(crate::Vec3: Copy,Clone);

rs_bindings_from_cc/test/golden/composable_bridging_rs_api_impl.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@
2020
#pragma clang diagnostic push
2121
#pragma clang diagnostic ignored "-Wthread-safety-analysis"
2222

23+
static_assert(sizeof(struct StructWithBridgeField) == 1);
24+
static_assert(alignof(struct StructWithBridgeField) == 1);
25+
static_assert(CRUBIT_OFFSET_OF(bridge_field, struct StructWithBridgeField) ==
26+
0);
27+
28+
extern "C" void __rust_thunk___ZN21StructWithBridgeFieldC1Ev(
29+
struct StructWithBridgeField* __this) {
30+
crubit::construct_at(__this);
31+
}
32+
2333
extern "C" void __rust_thunk___Z15ReturnCppStructv(
2434
unsigned char* __return_abi_buffer) {
2535
::crubit::Encoder __return_encoder(::crubit::CppStructAbi::kSize,

0 commit comments

Comments
 (0)