Skip to content

cpp_api_from_rust: NonNull<Ptr> -> pointer with crubit_nonnull in generated C++. - #2012

Open
copybara-service[bot] wants to merge 1 commit into
mainfrom
test_981303217
Open

copybara-service[bot] wants to merge 1 commit into
mainfrom
test_981303217

Conversation

@copybara-service

Copy link
Copy Markdown

cpp_api_from_rust: NonNull -> pointer with crubit_nonnull in generated C++.

rs_bindings_from_cc already wraps _Nonnull-annotated std::unique_ptr and std::shared_ptr in NonNull. This is the other direction: a NonNull<unique_ptr<T>> in a public Rust API is now spelled std::unique_ptr<T> crubit_nonnull in the generated header, rather than being emitted as an opaque Rust struct (and same for shared_ptr<T> and virtual_unique_ptr<T>).

NonNull<Ptr> is #[repr(transparent)] and its C++ spelling is just Ptr plus an attribute, so this needs no layout, ABI, or thunk changes. It is a cpp_layout_equivalent annotation with a {Ptr} placeholder.

The attribute is spelled crubit_nonnull rather than absl_nonnull. Both expand to Clang's _Nonnull, but crubit_nonnull comes from support/annotations_internal.h, which has no dependencies, so generated code does not acquire an Abseil dependency. It is also what cc_bindings_from_rs already emits for non-owning pointers.

Passing a NonNull-wrapped smart pointer by value additionally requires the movability fallback in format_legacy_bridged_type_with_placeholders to recognize it, because that check asks whether the generic argument is C++ move-constructible. The two smart pointers differ here: shared_ptr is Clone and so already qualifies, while unique_ptr has a Drop impl and implements neither Default nor Clone, so it does not. Note this exemption is not the same kind of claim as the existing unique_ptr/shared_ptr/vector entries, which state a true fact about containers whose move constructor never touches their element; NonNull's movability really is its argument's. It yields the right answer only because the check it suppresses would itself wrongly fail, and both halves are removed by the same cleanup (b/545883191).

This is gated on the existing nonnull_smart_pointers feature, the same one
that lets rs_bindings_from_cc produce NonNull from an _Nonnull-annotated
smart pointer. Gating both directions on one feature means a target opts into
the whole bridge at once, rather than being able to consume NonNull from C++
without producing it, or vice versa. Without the feature, NonNull is treated
as non-bridged, exactly as it was before it carried a cpp_type.

…erated C++.

`rs_bindings_from_cc` already wraps `_Nonnull`-annotated `std::unique_ptr` and `std::shared_ptr` in `NonNull`. This is the other direction: a `NonNull<unique_ptr<T>>` in a public Rust API is now spelled `std::unique_ptr<T> crubit_nonnull` in the generated header, rather than being emitted as an opaque Rust struct (and same for `shared_ptr<T>` and `virtual_unique_ptr<T>`).

`NonNull<Ptr>` is `#[repr(transparent)]` and its C++ spelling is just `Ptr` plus an attribute, so this needs no layout, ABI, or thunk changes. It is a `cpp_layout_equivalent` annotation with a `{Ptr}` placeholder.

The attribute is spelled `crubit_nonnull` rather than `absl_nonnull`. Both expand to Clang's `_Nonnull`, but `crubit_nonnull` comes from `support/annotations_internal.h`, which has no dependencies, so generated code does not acquire an Abseil dependency. It is also what `cc_bindings_from_rs` already emits for non-owning pointers.

Passing a `NonNull`-wrapped smart pointer by value additionally requires the movability fallback in `format_legacy_bridged_type_with_placeholders` to recognize it, because that check asks whether the *generic argument* is C++ move-constructible. The two smart pointers differ here: `shared_ptr` is `Clone` and so already qualifies, while `unique_ptr` has a `Drop` impl and implements neither `Default` nor `Clone`, so it does not. Note this exemption is not the same kind of claim as the existing `unique_ptr`/`shared_ptr`/`vector` entries, which state a true fact about containers whose move constructor never touches their element; `NonNull`'s movability really is its argument's. It yields the right answer only because the check it suppresses would itself wrongly fail, and both halves are removed by the same cleanup (b/545883191).

This is gated on the existing `nonnull_smart_pointers` feature, the same one
that lets `rs_bindings_from_cc` produce `NonNull` from an `_Nonnull`-annotated
smart pointer. Gating both directions on one feature means a target opts into
the whole bridge at once, rather than being able to consume `NonNull` from C++
without producing it, or vice versa. Without the feature, `NonNull` is treated
as non-bridged, exactly as it was before it carried a `cpp_type`.

PiperOrigin-RevId: 981303217
@google-cla

google-cla Bot commented Sep 14, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants