Skip to content

Commit

Permalink
Bridge Protobuf Rust's View and Mut message types to their C++ equiva…
Browse files Browse the repository at this point in the history
…lent types #crubit #cc_bindings_from_rs

== Functionality ==
Given a Rust function signature `pub fn handle_request(&mut self, req: FooRequestView, mut rsp: FooResponseMut) -> bool`, where `FooRequestView` and `FooResponseMut` are Protobuf Rust proxy types. This change enables Crubit to generate C++ bindings for `handle_request` with the C++ function signature `bool handle_request(const FooRequest* req, FooResponse* rsp)`.

== Crubit changes ==
Within a blaze build the Protobuf generated crate has two names. When the generated code gets compiled the name of the crate is the name of the `proto_library`. Later in the build the crate is renamed to the name of the `rust_proto_library`, which is what developers using Rust Protobufs see. So when the `cc_bindings_from_rs_aspect` runs the name of a Protobuf crate is the name of the `proto_library` and when the Crubit bindings get compiled the Protobuf crate in its dependencies has the name of the `rust_proto_library`. Therefore, in Crubit's generated code we rename the crate to its proto_library name via `extern crate` statements. We pass this information to Crubit via cmdline flag that gets set from within the build rules.

== Protobuf Rust changes ==
When building for the C++ kernel, Protobuf Rust View and Mut types get annotated with the `__crubit::annotate` attribute. The attribute describes the C++
message type to which the Rust type should be converted and it also includes the header that declares the C++ message type. Additionally, the type is marked `repr(transparent)` so that Crubit can verify that the ABI layout of a Protobuf Rust proxy type is pointer-like i.e. the view/mut structs have a single pointer field and any number of ZSTs. With this knowledge Crubit can generate code to convert the pointer-like Rust struct to a C++ pointer (and vice versa). No explicit conversion functions need to be specified.

An example of an annotation:

```
#[__crubit::annotate(
  cpp_type = "const FooRequest*",
  cpp_type_include = "path/to/foo_request.proto.h",
)]
#[repr(transparent)]
struct FooRequestView { ... }
```

PiperOrigin-RevId: 705850175
  • Loading branch information
buchgr authored and copybara-github committed Dec 13, 2024
1 parent bc16fe8 commit a2ef571
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 99 deletions.
12 changes: 12 additions & 0 deletions rust/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ load(
"rust_upb_proto_library_aspect",
)

ProtoCrateNamesInfo = provider(
doc = """A provider that contains both names a Protobuf crate has throughout the build.""",
fields = {
"crate_name": "The name of rust_proto_library.",
"old_crate_name": "The name of the proto_library.",
},
)

def rust_proto_library(name, deps, **args):
"""Declares all the boilerplate needed to use Rust protobufs conveniently.
Expand Down Expand Up @@ -112,6 +120,10 @@ def _rust_proto_library_impl(ctx):
crate_info_with_rust_proto_name = rust_common.crate_info(**fields)

return [
ProtoCrateNamesInfo(
crate_name = crate_info_with_rust_proto_name.name,
old_crate_name = crate_info.name,
),
crate_info_with_rust_proto_name,
dep_variant_info.dep_info,
dep_variant_info.cc_info,
Expand Down
202 changes: 103 additions & 99 deletions src/google/protobuf/compiler/rust/message.cc
Original file line number Diff line number Diff line change
Expand Up @@ -621,118 +621,122 @@ void GenerateRs(Context& ctx, const Descriptor& msg) {
return;
}
ctx.Emit(
{{"Msg", RsSafeName(msg.name())},
{"Msg::new", [&] { MessageNew(ctx, msg); }},
{"Msg::serialize", [&] { MessageSerialize(ctx, msg); }},
{"MsgMut::clear", [&] { MessageMutClear(ctx, msg); }},
{"Msg::clear_and_parse", [&] { MessageClearAndParse(ctx, msg); }},
{"Msg::drop", [&] { MessageDrop(ctx, msg); }},
{"Msg::debug", [&] { MessageDebug(ctx, msg); }},
{"MsgMut::merge_from", [&] { MessageMutMergeFrom(ctx, msg); }},
{"default_instance_impl",
[&] { GenerateDefaultInstanceImpl(ctx, msg); }},
{"accessor_fns",
[&] {
for (int i = 0; i < msg.field_count(); ++i) {
GenerateAccessorMsgImpl(ctx, *msg.field(i), AccessorCase::OWNED);
}
for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
GenerateOneofAccessors(ctx, *msg.real_oneof_decl(i),
AccessorCase::OWNED);
}
}},
{"nested_in_msg",
[&] {
// If we have no nested types, enums, or oneofs, bail out without
// emitting an empty mod some_msg.
if (msg.nested_type_count() == 0 && msg.enum_type_count() == 0 &&
msg.real_oneof_decl_count() == 0) {
return;
}
ctx.PushModule(RsSafeName(CamelToSnakeCase(msg.name())));
ctx.Emit({{"nested_msgs",
[&] {
for (int i = 0; i < msg.nested_type_count(); ++i) {
GenerateRs(ctx, *msg.nested_type(i));
}
}},
{"nested_enums",
[&] {
for (int i = 0; i < msg.enum_type_count(); ++i) {
GenerateEnumDefinition(ctx, *msg.enum_type(i));
}
}},
{"oneofs",
[&] {
for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
GenerateOneofDefinition(ctx, *msg.real_oneof_decl(i));
}
}}},
R"rs(
{
{"Msg", RsSafeName(msg.name())},
{"Msg::new", [&] { MessageNew(ctx, msg); }},
{"Msg::serialize", [&] { MessageSerialize(ctx, msg); }},
{"MsgMut::clear", [&] { MessageMutClear(ctx, msg); }},
{"Msg::clear_and_parse", [&] { MessageClearAndParse(ctx, msg); }},
{"Msg::drop", [&] { MessageDrop(ctx, msg); }},
{"Msg::debug", [&] { MessageDebug(ctx, msg); }},
{"MsgMut::merge_from", [&] { MessageMutMergeFrom(ctx, msg); }},
{"default_instance_impl",
[&] { GenerateDefaultInstanceImpl(ctx, msg); }},
{"accessor_fns",
[&] {
for (int i = 0; i < msg.field_count(); ++i) {
GenerateAccessorMsgImpl(ctx, *msg.field(i), AccessorCase::OWNED);
}
for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
GenerateOneofAccessors(ctx, *msg.real_oneof_decl(i),
AccessorCase::OWNED);
}
}},
{"nested_in_msg",
[&] {
// If we have no nested types, enums, or oneofs, bail out without
// emitting an empty mod some_msg.
if (msg.nested_type_count() == 0 && msg.enum_type_count() == 0 &&
msg.real_oneof_decl_count() == 0) {
return;
}
ctx.PushModule(RsSafeName(CamelToSnakeCase(msg.name())));
ctx.Emit(
{{"nested_msgs",
[&] {
for (int i = 0; i < msg.nested_type_count(); ++i) {
GenerateRs(ctx, *msg.nested_type(i));
}
}},
{"nested_enums",
[&] {
for (int i = 0; i < msg.enum_type_count(); ++i) {
GenerateEnumDefinition(ctx, *msg.enum_type(i));
}
}},
{"oneofs",
[&] {
for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
GenerateOneofDefinition(ctx, *msg.real_oneof_decl(i));
}
}}},
R"rs(
$nested_msgs$
$nested_enums$
$oneofs$
)rs");
ctx.PopModule();
}},
{"raw_arena_getter_for_message",
[&] {
if (ctx.is_upb()) {
ctx.Emit({}, R"rs(
ctx.PopModule();
}},
{"raw_arena_getter_for_message",
[&] {
if (ctx.is_upb()) {
ctx.Emit({}, R"rs(
fn arena(&self) -> &$pbr$::Arena {
&self.inner.arena
}
)rs");
}
}},
{"raw_arena_getter_for_msgmut",
[&] {
if (ctx.is_upb()) {
ctx.Emit({}, R"rs(
}
}},
{"raw_arena_getter_for_msgmut",
[&] {
if (ctx.is_upb()) {
ctx.Emit({}, R"rs(
fn arena(&self) -> &$pbr$::Arena {
self.inner.arena()
}
)rs");
}
}},
{"accessor_fns_for_views",
[&] {
for (int i = 0; i < msg.field_count(); ++i) {
GenerateAccessorMsgImpl(ctx, *msg.field(i), AccessorCase::VIEW);
}
for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
GenerateOneofAccessors(ctx, *msg.real_oneof_decl(i),
AccessorCase::VIEW);
}
}},
{"accessor_fns_for_muts",
[&] {
for (int i = 0; i < msg.field_count(); ++i) {
GenerateAccessorMsgImpl(ctx, *msg.field(i), AccessorCase::MUT);
}
for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
GenerateOneofAccessors(ctx, *msg.real_oneof_decl(i),
AccessorCase::MUT);
}
}},
{"into_proxied_impl", [&] { IntoProxiedForMessage(ctx, msg); }},
{"upb_generated_message_trait_impls",
[&] { UpbGeneratedMessageTraitImpls(ctx, msg); }},
{"repeated_impl", [&] { MessageProxiedInRepeated(ctx, msg); }},
{"type_conversions_impl", [&] { TypeConversions(ctx, msg); }},
{"unwrap_upb",
[&] {
if (ctx.is_upb()) {
ctx.Emit(".unwrap_or_else(||$pbr$::ScratchSpace::zeroed_block())");
}
}},
{"upb_arena",
[&] {
if (ctx.is_upb()) {
ctx.Emit(", inner.msg_ref().arena().raw()");
}
}}},
}
}},
{"accessor_fns_for_views",
[&] {
for (int i = 0; i < msg.field_count(); ++i) {
GenerateAccessorMsgImpl(ctx, *msg.field(i), AccessorCase::VIEW);
}
for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
GenerateOneofAccessors(ctx, *msg.real_oneof_decl(i),
AccessorCase::VIEW);
}
}},
{"accessor_fns_for_muts",
[&] {
for (int i = 0; i < msg.field_count(); ++i) {
GenerateAccessorMsgImpl(ctx, *msg.field(i), AccessorCase::MUT);
}
for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
GenerateOneofAccessors(ctx, *msg.real_oneof_decl(i),
AccessorCase::MUT);
}
}},
{"into_proxied_impl", [&] { IntoProxiedForMessage(ctx, msg); }},
{"upb_generated_message_trait_impls",
[&] { UpbGeneratedMessageTraitImpls(ctx, msg); }},
{"repeated_impl", [&] { MessageProxiedInRepeated(ctx, msg); }},
{"type_conversions_impl", [&] { TypeConversions(ctx, msg); }},
{"unwrap_upb",
[&] {
if (ctx.is_upb()) {
ctx.Emit(
".unwrap_or_else(||$pbr$::ScratchSpace::zeroed_block())");
}
}},
{"upb_arena",
[&] {
if (ctx.is_upb()) {
ctx.Emit(", inner.msg_ref().arena().raw()");
}
}},
},
R"rs(
#[allow(non_camel_case_types)]
pub struct $Msg$ {
Expand Down

0 comments on commit a2ef571

Please sign in to comment.