Merge remote-tracking branch 'remotes/upstream/main' into merge-upb

pull/13686/head
Adam Cozzette 2 years ago
commit 100e9d3ab6
  1. 4
      protobuf_deps.bzl
  2. 16
      python/google/protobuf/internal/message_test.py
  3. 46
      rust/internal.rs
  4. 39
      rust/macros.rs
  5. 23
      rust/test/shared/accessors_proto3_test.rs
  6. 22
      rust/test/shared/accessors_test.rs
  7. 20
      src/google/protobuf/compiler/rust/BUILD.bazel
  8. 4
      src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
  9. 89
      src/google/protobuf/compiler/rust/message.cc
  10. 35
      src/google/protobuf/compiler/rust/naming.cc
  11. 5
      src/google/protobuf/compiler/rust/naming.h
  12. 250
      src/google/protobuf/compiler/rust/oneof.cc
  13. 52
      src/google/protobuf/compiler/rust/oneof.h

@ -150,6 +150,6 @@ def protobuf_deps():
_github_archive(
name = "upb",
repo = "https://github.com/protocolbuffers/upb",
commit = "823a12e80934f9c8fb9178191d69f06a1bbfd604",
sha256 = "165f425714d32018a1a856b1935cfa24206878f8a469384808d2dce1d7c4003a",
commit = "57636ce03ac1e2aab3a362a61a6664981e21cda5",
sha256 = "75da534db927e165cb550d232c35e2dec4b5153b3b47989b250d75febbfc1710",
)

@ -422,6 +422,22 @@ class MessageTest(unittest.TestCase):
empty.ParseFromString(populated.SerializeToString())
self.assertEqual(str(empty), '')
def testCopyFromEmpty(self, message_module):
msg = message_module.NestedTestAllTypes()
test_msg = message_module.NestedTestAllTypes()
test_util.SetAllFields(test_msg.payload)
self.assertTrue(test_msg.HasField('payload'))
# Copy from empty message
test_msg.CopyFrom(msg)
self.assertEqual(0, len(test_msg.ListFields()))
test_util.SetAllFields(test_msg.payload)
self.assertTrue(test_msg.HasField('payload'))
# Copy from a non exist message
test_msg.CopyFrom(msg.child)
self.assertFalse(test_msg.HasField('payload'))
self.assertEqual(0, len(test_msg.ListFields()))
def testAppendRepeatedCompositeField(self, message_module):
msg = message_module.TestAllTypes()
msg.repeated_nested_message.append(

@ -32,22 +32,54 @@
//! exposed to through the `protobuf` path but must be public for use by
//! generated code.
use crate::macros::define_opaque_nonnulls;
pub use crate::vtable::{
new_vtable_field_entry, BytesMutVTable, BytesOptionalMutVTable, RawVTableMutator,
};
use std::ptr::NonNull;
use std::slice;
/// Used to protect internal-only items from being used accidentally.
pub struct Private;
define_opaque_nonnulls!(
/// A raw pointer to the underlying arena for this runtime.
pub type RawArena = NonNull<RawArenaData>;
/// Defines a set of opaque, unique, non-accessible pointees.
///
/// The [Rustonomicon][nomicon] currently recommends a zero-sized struct,
/// though this should use [`extern type`] when that is stabilized.
/// [nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
/// [`extern type`]: https://github.com/rust-lang/rust/issues/43467
mod _opaque_pointees {
/// Opaque pointee for [`RawMessage`]
///
/// This type is not meant to be dereferenced in Rust code.
/// It is only meant to provide type safety for raw pointers
/// which are manipulated behind FFI.
///
/// [`RawMessage`]: super::RawMessage
#[repr(C)]
pub struct RawMessageData {
_data: [u8; 0],
_marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
}
/// Opaque pointee for [`RawArena`]
///
/// This type is not meant to be dereferenced in Rust code.
/// It is only meant to provide type safety for raw pointers
/// which are manipulated behind FFI.
///
/// [`RawArena`]: super::RawArena
#[repr(C)]
pub struct RawArenaData {
_data: [u8; 0],
_marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
}
}
/// A raw pointer to the underlying message for this runtime.
pub type RawMessage = NonNull<_opaque_pointees::RawMessageData>;
/// A raw pointer to the underlying message for this runtime.
pub type RawMessage = NonNull<RawMessageData>;
);
/// A raw pointer to the underlying arena for this runtime.
pub type RawArena = NonNull<_opaque_pointees::RawArenaData>;
/// Represents an ABI-stable version of `NonNull<[u8]>`/`string_view` (a
/// borrowed slice of bytes) for FFI use only.

@ -30,45 +30,6 @@
//! Runtime-internal macros
/// Defines a set of opaque pointers and a unique non-accessible pointees.
///
/// This provides a type safety benefit over using `NonNull<u8>` everywhere.
/// The [Rustonomicon][nomicon] currently recommends a zero-sized struct,
/// though this should use [`extern type`] when that is stabilized.
///
/// Because this defines a new private module, it can only be called once per
/// module.
///
/// [nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
/// [`extern type`]: https://github.com/rust-lang/rust/issues/43467
macro_rules! define_opaque_nonnulls {
($($(#[$meta:meta])* $vis:vis type $name:ident = NonNull<$raw_name:ident>;)*) => {
mod _opaque_pointees {
$(
#[doc = concat!("Opaque pointee for [`", stringify!($name), "`][pointer]")]
///
/// This type is not meant to be dereferenced in Rust code.
/// It is only meant to provide type safety for raw pointers
/// which are manipulated behind FFI.
#[doc = concat!("[pointer]: super::", stringify!($name))]
#[repr(C)]
pub struct $raw_name {
_data: [u8; 0],
_marker: ::std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
}
)*
}
$(
$(#[$meta])*
///
/// This is an opaque pointer used for FFI:
/// do not dereference this type in Rust code.
$vis type $name = ::std::ptr::NonNull<_opaque_pointees::$raw_name>;
)*
};
}
pub(crate) use define_opaque_nonnulls;
/// Defines a `impl SettableValue<$proxied> for SomeType` body that forwards to
/// another implementation.
///

@ -31,7 +31,7 @@
/// Tests covering accessors for singular bool, int32, int64, and bytes fields
/// on proto3.
use protobuf::Optional;
use unittest_proto3::proto3_unittest::TestAllTypes;
use unittest_proto3::proto3_unittest::{TestAllTypes, TestAllTypes_};
use unittest_proto3_optional::proto2_unittest::TestProto3Optional;
#[test]
@ -119,3 +119,24 @@ fn test_optional_bytes_accessors() {
assert_eq!(msg.optional_bytes_mut().get(), b"\xffbinary\x85non-utf8");
assert_eq!(msg.optional_bytes_mut().or_default().get(), b"\xffbinary\x85non-utf8");
}
#[test]
fn test_oneof_accessors() {
let mut msg = TestAllTypes::new();
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set);
msg.oneof_uint32_set(Some(7));
assert_eq!(msg.oneof_uint32_opt(), Some(7));
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::OneofUint32(7));
msg.oneof_uint32_set(None);
assert_eq!(msg.oneof_uint32_opt(), None);
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set);
msg.oneof_uint32_set(Some(7));
msg.oneof_bytes_mut().set(b"");
assert_eq!(msg.oneof_uint32_opt(), None);
// This should show it set to the OneofBytes but its not supported yet.
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set);
}

@ -31,7 +31,7 @@
//! Tests covering accessors for singular bool, int32, int64, and bytes fields.
use protobuf::Optional;
use unittest_proto::proto2_unittest::TestAllTypes;
use unittest_proto::proto2_unittest::{TestAllTypes, TestAllTypes_};
#[test]
fn test_default_accessors() {
@ -314,3 +314,23 @@ fn test_singular_msg_field() {
// call should look like msg.optional_nested_message().bb()
let _msg: unittest_proto::proto2_unittest::TestAllTypesView = msg.optional_nested_message();
}
#[test]
fn test_oneof_accessors() {
let mut msg = TestAllTypes::new();
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set);
msg.oneof_uint32_set(Some(7));
assert_eq!(msg.oneof_uint32_opt(), Some(7));
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::OneofUint32(7));
msg.oneof_uint32_set(None);
assert_eq!(msg.oneof_uint32_opt(), None);
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set);
msg.oneof_uint32_set(Some(7));
msg.oneof_bytes_mut().set(b"");
assert_eq!(msg.oneof_uint32_opt(), None);
// This should show it set to the OneofBytes but its not supported yet.
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set);
}

@ -39,6 +39,7 @@ cc_library(
":accessors",
":context",
":naming",
":oneof",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler/cpp:names",
"@com_google_absl//absl/log:absl_check",
@ -56,8 +57,8 @@ cc_library(
"accessors/unsupported_field.cc",
],
hdrs = [
"accessors/accessors.h",
"accessors/accessor_generator.h",
"accessors/accessor_generator.h",
"accessors/accessors.h",
],
copts = COPTS,
include_prefix = "google/protobuf/compiler/rust",
@ -103,6 +104,21 @@ cc_library(
],
)
cc_library(
name = "oneof",
srcs = ["oneof.cc"],
hdrs = ["oneof.h"],
copts = COPTS,
include_prefix = "google/protobuf/compiler/rust",
deps = [
":context",
":naming",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler/cpp:names",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "relative_path",
srcs = ["relative_path.cc"],

@ -44,7 +44,7 @@ void SingularScalar::InMsgImpl(Context<FieldDescriptor> field) const {
field.Emit(
{
{"field", field.desc().name()},
{"Scalar", PrimitiveRsTypeName(field)},
{"Scalar", PrimitiveRsTypeName(field.desc())},
{"hazzer_thunk", Thunk(field, "has")},
{"getter",
[&] {
@ -86,7 +86,7 @@ void SingularScalar::InMsgImpl(Context<FieldDescriptor> field) const {
void SingularScalar::InExternC(Context<FieldDescriptor> field) const {
field.Emit(
{{"Scalar", PrimitiveRsTypeName(field)},
{{"Scalar", PrimitiveRsTypeName(field.desc())},
{"hazzer_thunk", Thunk(field, "has")},
{"getter_thunk", Thunk(field, "get")},
{"setter_thunk", Thunk(field, "set")},

@ -38,6 +38,7 @@
#include "google/protobuf/compiler/rust/accessors/accessors.h"
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/compiler/rust/naming.h"
#include "google/protobuf/compiler/rust/oneof.h"
#include "google/protobuf/descriptor.h"
namespace google {
@ -206,11 +207,18 @@ void GenerateRs(Context<Descriptor> msg) {
msg.Emit({{"comment", FieldInfoComment(field)}}, R"rs(
// $comment$
)rs");
GenerateAccessorMsgImpl(field);
msg.printer().PrintRaw("\n");
}
}},
{"oneof_accessor_fns",
[&] {
for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) {
GenerateOneofAccessors(
msg.WithDesc(*msg.desc().real_oneof_decl(i)));
msg.printer().PrintRaw("\n");
}
}},
{"accessor_externs",
[&] {
for (int i = 0; i < msg.desc().field_count(); ++i) {
@ -218,9 +226,20 @@ void GenerateRs(Context<Descriptor> msg) {
msg.printer().PrintRaw("\n");
}
}},
{"oneof_externs",
[&] {
for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) {
GenerateOneofExternC(
msg.WithDesc(*msg.desc().real_oneof_decl(i)));
msg.printer().PrintRaw("\n");
}
}},
{"nested_msgs",
[&] {
if (msg.desc().nested_type_count() == 0) {
// If we have no nested types or oneofs, bail out without emitting
// an empty mod SomeMsg_.
if (msg.desc().nested_type_count() == 0 &&
msg.desc().real_oneof_decl_count() == 0) {
return;
}
msg.Emit({{"Msg", msg.desc().name()},
@ -232,10 +251,20 @@ void GenerateRs(Context<Descriptor> msg) {
msg.WithDesc(msg.desc().nested_type(i));
GenerateRs(nested_msg);
}
}},
{"oneofs",
[&] {
for (int i = 0;
i < msg.desc().real_oneof_decl_count(); ++i) {
GenerateOneofDefinition(
msg.WithDesc(*msg.desc().real_oneof_decl(i)));
}
}}},
R"rs(
pub mod $Msg$_ {
$nested_msgs$
$oneofs$
} // mod $Msg$_
)rs");
}},
@ -334,6 +363,8 @@ void GenerateRs(Context<Descriptor> msg) {
}
$accessor_fns$
$oneof_accessor_fns$
} // impl $Msg$
//~ We implement drop unconditionally, so that `$Msg$: Drop` regardless
@ -348,6 +379,8 @@ void GenerateRs(Context<Descriptor> msg) {
$Msg_externs$
$accessor_externs$
$oneof_externs$
} // extern "C" for $Msg$
$nested_msgs$
@ -377,29 +410,33 @@ void GenerateThunksCc(Context<Descriptor> msg) {
}
msg.Emit(
{
{"abi", "\"C\""}, // Workaround for syntax highlight bug in VSCode.
{"Msg", msg.desc().name()},
{"QualifiedMsg", cpp::QualifiedClassName(&msg.desc())},
{"new_thunk", Thunk(msg, "new")},
{"delete_thunk", Thunk(msg, "delete")},
{"serialize_thunk", Thunk(msg, "serialize")},
{"deserialize_thunk", Thunk(msg, "deserialize")},
{"nested_msg_thunks",
[&] {
for (int i = 0; i < msg.desc().nested_type_count(); ++i) {
Context<Descriptor> nested_msg =
msg.WithDesc(msg.desc().nested_type(i));
GenerateThunksCc(nested_msg);
}
}},
{"accessor_thunks",
[&] {
for (int i = 0; i < msg.desc().field_count(); ++i) {
GenerateAccessorThunkCc(msg.WithDesc(*msg.desc().field(i)));
}
}},
},
{{"abi", "\"C\""}, // Workaround for syntax highlight bug in VSCode.
{"Msg", msg.desc().name()},
{"QualifiedMsg", cpp::QualifiedClassName(&msg.desc())},
{"new_thunk", Thunk(msg, "new")},
{"delete_thunk", Thunk(msg, "delete")},
{"serialize_thunk", Thunk(msg, "serialize")},
{"deserialize_thunk", Thunk(msg, "deserialize")},
{"nested_msg_thunks",
[&] {
for (int i = 0; i < msg.desc().nested_type_count(); ++i) {
Context<Descriptor> nested_msg =
msg.WithDesc(msg.desc().nested_type(i));
GenerateThunksCc(nested_msg);
}
}},
{"accessor_thunks",
[&] {
for (int i = 0; i < msg.desc().field_count(); ++i) {
GenerateAccessorThunkCc(msg.WithDesc(*msg.desc().field(i)));
}
}},
{"oneof_thunks",
[&] {
for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) {
GenerateOneofThunkCc(msg.WithDesc(*msg.desc().real_oneof_decl(i)));
}
}}},
R"cc(
//~ $abi$ is a workaround for a syntax highlight bug in VSCode. However,
//~ that confuses clang-format (it refuses to keep the newline after
@ -417,6 +454,8 @@ void GenerateThunksCc(Context<Descriptor> msg) {
}
$accessor_thunks$
$oneof_thunks$
} // extern $abi$
// clang-format on

@ -36,7 +36,6 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/rust/context.h"
@ -83,32 +82,51 @@ std::string GetHeaderFile(Context<FileDescriptor> file) {
return absl::StrCat(basename, ".proto.h");
}
std::string Thunk(Context<FieldDescriptor> field, absl::string_view op) {
namespace {
template <typename T>
std::string Thunk(Context<T> field, absl::string_view op) {
// NOTE: When field.is_upb(), this functions outputs must match the symbols
// that the upbc plugin generates exactly. Failure to do so correctly results
// in a link-time failure.
absl::string_view prefix = field.is_cpp() ? "__rust_proto_thunk__" : "";
std::string thunk =
absl::StrCat(prefix, GetUnderscoreDelimitedFullName(
field.WithDesc(field.desc().containing_type())));
absl::string_view format;
if (field.is_upb() && op == "get") {
absl::SubstituteAndAppend(&thunk, "_$0", field.desc().name());
// upb getter is simply the field name (no "get" in the name).
format = "_$1";
} else if (field.is_upb() && op == "case") {
// upb oneof case function is x_case compared to has/set/clear which are in
// the other order e.g. clear_x.
format = "_$1_$0";
} else {
absl::SubstituteAndAppend(&thunk, "_$0_$1", op, field.desc().name());
format = "_$0_$1";
}
absl::SubstituteAndAppend(&thunk, format, op, field.desc().name());
return thunk;
}
} // namespace
std::string Thunk(Context<FieldDescriptor> field, absl::string_view op) {
return Thunk<FieldDescriptor>(field, op);
}
std::string Thunk(Context<OneofDescriptor> field, absl::string_view op) {
return Thunk<OneofDescriptor>(field, op);
}
std::string Thunk(Context<Descriptor> msg, absl::string_view op) {
absl::string_view prefix = msg.is_cpp() ? "__rust_proto_thunk__" : "";
return absl::StrCat(prefix, GetUnderscoreDelimitedFullName(msg), "_", op);
}
absl::string_view PrimitiveRsTypeName(Context<FieldDescriptor> field) {
switch (field.desc().type()) {
std::string PrimitiveRsTypeName(const FieldDescriptor& desc) {
switch (desc.type()) {
case FieldDescriptor::TYPE_BOOL:
return "bool";
case FieldDescriptor::TYPE_INT32:
@ -134,7 +152,7 @@ absl::string_view PrimitiveRsTypeName(Context<FieldDescriptor> field) {
default:
break;
}
ABSL_LOG(FATAL) << "Unsupported field type: " << field.desc().type_name();
ABSL_LOG(FATAL) << "Unsupported field type: " << desc.type_name();
return "";
}
@ -173,6 +191,7 @@ std::string FieldInfoComment(Context<FieldDescriptor> field) {
return comment;
}
} // namespace rust
} // namespace compiler
} // namespace protobuf

@ -49,9 +49,11 @@ std::string GetThunkCcFile(Context<FileDescriptor> file);
std::string GetHeaderFile(Context<FileDescriptor> file);
std::string Thunk(Context<FieldDescriptor> field, absl::string_view op);
std::string Thunk(Context<OneofDescriptor> field, absl::string_view op);
std::string Thunk(Context<Descriptor> msg, absl::string_view op);
absl::string_view PrimitiveRsTypeName(Context<FieldDescriptor> field);
std::string PrimitiveRsTypeName(const FieldDescriptor& desc);
std::string FieldInfoComment(Context<FieldDescriptor> field);
@ -59,6 +61,7 @@ std::string RustModule(Context<Descriptor> msg);
std::string RustInternalModuleName(Context<FileDescriptor> file);
std::string GetCrateRelativeQualifiedPath(Context<Descriptor> msg);
} // namespace rust
} // namespace compiler
} // namespace protobuf

@ -0,0 +1,250 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "google/protobuf/compiler/rust/oneof.h"
#include <string>
#include "absl/strings/string_view.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/compiler/rust/naming.h"
#include "google/protobuf/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
// We emit three Rust enums:
// - An enum acting as a tagged union that has each case holds a View<> of
// each of the cases. Named as the one_of name in CamelCase.
// - An enum acting as a tagged union that has each case holds a Mut<> of
// each of the cases. Named as one_of name in CamelCase with "Mut" appended.
// [TODO(b/285309334): Mut not implemented yet].
// - A simple enum whose cases have int values matching the cpp or upb's
// case enum. Named as the one_of camelcase with "Case" appended.
// All three contain cases matching the fields in the oneof CamelCased.
// The first and second are exposed in the API, the third is internal and
// used for interop with the Kernels in the generation of the other two.
//
// Example:
// For this oneof:
// message SomeMsg {
// oneof some_oneof {
// int32 field_a = 7;
// uint32 field_b = 9;
// }
// }
//
// This will emit as the exposed API:
// pub mod SomeMsg_ {
// // The 'view' struct (no suffix on the name)
// pub enum SomeOneof {
// FieldA(i32) = 7,
// FieldB(u32) = 9,
// not_set = 0
// }
// }
// impl SomeMsg {
// pub fn some_oneof() -> SomeOneof {...}
// }
//
// An additional "Case" enum which just reflects the corresponding slot numbers
// is emitted for usage with the FFI (exactly matching the Case struct that both
// cpp and upb generate). This enum is pub(super) which makes it private to the
// codegen since its inside an inner mod:
//
// #[repr(C)] pub(super) enum SomeOneofCase {
// FieldA = 7,
// FieldB = 9,
// not_set = 0
// }
namespace {
std::string ToCamelCase(absl::string_view name) {
return cpp::UnderscoresToCamelCase(name, /* upper initial letter */ true);
}
std::string oneofViewEnumRsName(const OneofDescriptor& desc) {
return ToCamelCase(desc.name());
}
std::string oneofCaseEnumName(const OneofDescriptor& desc) {
// Note: This is the name used for the cpp Case enum, we use it for both
// the Rust Case enum as well as for the cpp case enum in the cpp thunk.
return oneofViewEnumRsName(desc) + "Case";
}
// TODO(b/285309334): Promote up to naming.h once all types can be spelled.
std::string RsTypeName(const FieldDescriptor& desc) {
switch (desc.type()) {
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_GROUP:
case FieldDescriptor::TYPE_STRING:
case FieldDescriptor::TYPE_BYTES:
return "";
default:
return PrimitiveRsTypeName(desc);
}
}
} // namespace
void GenerateOneofDefinition(Context<OneofDescriptor> oneof) {
const auto& desc = oneof.desc();
oneof.Emit(
{{"view_enum_name", oneofViewEnumRsName(desc)},
{"view_fields",
[&] {
for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i);
std::string rs_type = RsTypeName(*field);
if (rs_type.empty()) {
continue;
}
oneof.Emit({{"name", ToCamelCase(field->name())},
{"type", rs_type},
{"number", std::to_string(field->number())}},
R"rs($name$($type$) = $number$,
)rs");
}
}}},
// TODO(b/297342638): Revisit if isize is the optimal repr for this enum.
R"rs(
#[non_exhaustive]
#[derive(Debug, PartialEq)]
#[repr(isize)]
pub enum $view_enum_name$ {
$view_fields$
#[allow(non_camel_case_types)]
not_set = 0
}
)rs");
// Note: This enum is used as the Thunk return type for getting which case is
// used: it exactly matches the generate case enum that both cpp and upb use.
oneof.Emit({{"case_enum_name", oneofCaseEnumName(desc)},
{"cases",
[&] {
for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i);
oneof.Emit({{"name", ToCamelCase(field->name())},
{"number", std::to_string(field->number())}},
R"rs($name$ = $number$,
)rs");
}
}}},
R"rs(
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub(super) enum $case_enum_name$ {
$cases$
#[allow(non_camel_case_types)]
not_set = 0
}
)rs");
}
void GenerateOneofAccessors(Context<OneofDescriptor> oneof) {
const auto& desc = oneof.desc();
oneof.Emit(
{{"oneof_name", desc.name()},
{"view_enum_name", oneofViewEnumRsName(desc)},
{"case_enum_name", oneofCaseEnumName(desc)},
{"cases",
[&] {
for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i);
std::string rs_type = RsTypeName(*field);
if (rs_type.empty()) {
continue;
}
oneof.Emit(
{
{"case", ToCamelCase(field->name())},
{"rs_getter", field->name()},
{"type", rs_type},
},
R"rs($Msg$_::$case_enum_name$::$case$ => $Msg$_::$view_enum_name$::$case$(self.$rs_getter$()),
)rs");
}
}},
{"case_thunk", Thunk(oneof, "case")}},
R"rs(
pub fn r#$oneof_name$(&self) -> $Msg$_::$view_enum_name$ {
match unsafe { $case_thunk$(self.inner.msg) } {
$cases$
_ => $Msg$_::$view_enum_name$::not_set
}
}
)rs");
}
void GenerateOneofExternC(Context<OneofDescriptor> oneof) {
const auto& desc = oneof.desc();
oneof.Emit(
{
{"case_enum_rs_name", oneofCaseEnumName(desc)},
{"case_thunk", Thunk(oneof, "case")},
},
R"rs(
fn $case_thunk$(raw_msg: $pbi$::RawMessage) -> $Msg$_::$case_enum_rs_name$;
)rs");
}
void GenerateOneofThunkCc(Context<OneofDescriptor> oneof) {
const auto& desc = oneof.desc();
oneof.Emit(
{
{"oneof_name", desc.name()},
{"case_enum_name", oneofCaseEnumName(desc)},
{"case_thunk", Thunk(oneof, "case")},
{"QualifiedMsg", cpp::QualifiedClassName(desc.containing_type())},
},
R"cc(
$QualifiedMsg$::$case_enum_name$ $case_thunk$($QualifiedMsg$* msg) {
return msg->$oneof_name$_case();
}
)cc");
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,52 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_ONEOF_H__
#define GOOGLE_PROTOBUF_COMPILER_RUST_ONEOF_H__
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
void GenerateOneofDefinition(Context<OneofDescriptor> oneof);
void GenerateOneofAccessors(Context<OneofDescriptor> oneof);
void GenerateOneofExternC(Context<OneofDescriptor> oneof);
void GenerateOneofThunkCc(Context<OneofDescriptor> oneof);
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_RUST_ONEOF_H__
Loading…
Cancel
Save