diff --git a/src/google/protobuf/compiler/rust/BUILD.bazel b/src/google/protobuf/compiler/rust/BUILD.bazel index fd28df6dc8..7adde60df1 100644 --- a/src/google/protobuf/compiler/rust/BUILD.bazel +++ b/src/google/protobuf/compiler/rust/BUILD.bazel @@ -178,6 +178,7 @@ cc_library( "@com_google_absl//absl/log:absl_check", "@com_google_absl//absl/log:absl_log", "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", ], ) diff --git a/src/google/protobuf/compiler/rust/accessors/BUILD.bazel b/src/google/protobuf/compiler/rust/accessors/BUILD.bazel index f5ff57e72a..e293025645 100644 --- a/src/google/protobuf/compiler/rust/accessors/BUILD.bazel +++ b/src/google/protobuf/compiler/rust/accessors/BUILD.bazel @@ -18,12 +18,14 @@ cc_library( "singular_scalar.cc", "singular_string.cc", "unsupported_field.cc", + "with_presence.cc", ], hdrs = [ "accessor_case.h", "accessors.h", "default_value.h", "generator.h", + "with_presence.h", ], copts = COPTS, strip_include_prefix = "/src", diff --git a/src/google/protobuf/compiler/rust/accessors/accessors.cc b/src/google/protobuf/compiler/rust/accessors/accessors.cc index 12a5fd72c3..87c88406c6 100644 --- a/src/google/protobuf/compiler/rust/accessors/accessors.cc +++ b/src/google/protobuf/compiler/rust/accessors/accessors.cc @@ -65,6 +65,7 @@ std::unique_ptr AccessorGeneratorFor( } ABSL_LOG(FATAL) << "Unexpected field type: " << field.type(); + return nullptr; } } // namespace diff --git a/src/google/protobuf/compiler/rust/accessors/singular_cord.cc b/src/google/protobuf/compiler/rust/accessors/singular_cord.cc index cb9d486a82..7fa9a0056d 100644 --- a/src/google/protobuf/compiler/rust/accessors/singular_cord.cc +++ b/src/google/protobuf/compiler/rust/accessors/singular_cord.cc @@ -11,6 +11,7 @@ #include "google/protobuf/compiler/cpp/helpers.h" #include "google/protobuf/compiler/rust/accessors/accessor_case.h" #include "google/protobuf/compiler/rust/accessors/generator.h" +#include "google/protobuf/compiler/rust/accessors/with_presence.h" #include "google/protobuf/compiler/rust/context.h" #include "google/protobuf/compiler/rust/naming.h" #include "google/protobuf/descriptor.h" @@ -22,17 +23,19 @@ namespace rust { void SingularCord::InMsgImpl(Context& ctx, const FieldDescriptor& field, AccessorCase accessor_case) const { + if (field.has_presence()) { + WithPresenceAccessorsInMsgImpl(ctx, field, accessor_case); + } + std::string field_name = FieldNameWithCollisionAvoidance(field); bool is_string_type = field.type() == FieldDescriptor::TYPE_STRING; ctx.Emit( {{"field", RsSafeName(field_name)}, {"raw_field_name", field_name}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, {"borrowed_getter_thunk", ThunkName(ctx, field, "get_cord_borrowed")}, {"owned_getter_thunk", ThunkName(ctx, field, "get_cord_owned")}, {"is_flat_thunk", ThunkName(ctx, field, "cord_is_flat")}, {"setter_thunk", ThunkName(ctx, field, "set")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, {"getter_thunk", ThunkName(ctx, field, "get")}, {"proxied_type", RsTypePath(ctx, field)}, {"borrowed_type", @@ -159,36 +162,20 @@ void SingularCord::InMsgImpl(Context& ctx, const FieldDescriptor& field, $setter_impl$ } )rs"); - }}, - {"hazzer", - [&] { - if (!field.has_presence()) return; - ctx.Emit({}, R"rs( - pub fn has_$raw_field_name$($view_self$) -> bool { - unsafe { $hazzer_thunk$(self.raw_msg()) } - })rs"); - }}, - {"clearer", - [&] { - if (accessor_case == AccessorCase::VIEW) return; - if (!field.has_presence()) return; - ctx.Emit({}, R"rs( - pub fn clear_$raw_field_name$(&mut self) { - unsafe { $clearer_thunk$(self.raw_msg()) } - })rs"); }}}, R"rs( $getter$ $setter$ - $hazzer$ - $clearer$ )rs"); } void SingularCord::InExternC(Context& ctx, const FieldDescriptor& field) const { + if (field.has_presence()) { + WithPresenceAccessorsInExternC(ctx, field); + } + ctx.Emit( - {{"hazzer_thunk", ThunkName(ctx, field, "has")}, - {"borrowed_getter_thunk", ThunkName(ctx, field, "get_cord_borrowed")}, + {{"borrowed_getter_thunk", ThunkName(ctx, field, "get_cord_borrowed")}, {"owned_getter_thunk", ThunkName(ctx, field, "get_cord_owned")}, {"is_flat_thunk", ThunkName(ctx, field, "cord_is_flat")}, {"getter_thunk", ThunkName(ctx, field, "get")}, @@ -205,7 +192,6 @@ void SingularCord::InExternC(Context& ctx, const FieldDescriptor& field) const { )rs"); } }}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, {"getter_thunks", [&] { if (ctx.is_cpp()) { @@ -219,47 +205,26 @@ void SingularCord::InExternC(Context& ctx, const FieldDescriptor& field) const { fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $pbr$::PtrAndLen; )rs"); } - }}, - {"with_presence_fields_thunks", - [&] { - if (field.has_presence()) { - ctx.Emit(R"rs( - fn $hazzer_thunk$(raw_msg: $pbr$::RawMessage) -> bool; - fn $clearer_thunk$(raw_msg: $pbr$::RawMessage); - )rs"); - } }}}, R"rs( - $with_presence_fields_thunks$ $getter_thunks$ $setter$ )rs"); } void SingularCord::InThunkCc(Context& ctx, const FieldDescriptor& field) const { + if (field.has_presence()) { + WithPresenceAccessorsInThunkCc(ctx, field); + } + ctx.Emit( {{"field", cpp::FieldName(&field)}, {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, {"setter_thunk", ThunkName(ctx, field, "set")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, {"borrowed_getter_thunk", ThunkName(ctx, field, "get_cord_borrowed")}, {"owned_getter_thunk", ThunkName(ctx, field, "get_cord_owned")}, - {"is_flat_thunk", ThunkName(ctx, field, "cord_is_flat")}, - {"with_presence_fields_thunks", - [&] { - if (field.has_presence()) { - ctx.Emit(R"cc( - bool $hazzer_thunk$($QualifiedMsg$* msg) { - return msg->has_$field$(); - } - void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); } - )cc"); - } - }}}, + {"is_flat_thunk", ThunkName(ctx, field, "cord_is_flat")}}, R"cc( - $with_presence_fields_thunks$; - bool $is_flat_thunk$($QualifiedMsg$* msg) { const absl::Cord& cord = msg->$field$(); return cord.TryFlat().has_value(); diff --git a/src/google/protobuf/compiler/rust/accessors/singular_message.cc b/src/google/protobuf/compiler/rust/accessors/singular_message.cc index 27099d16a6..b6752ebb04 100644 --- a/src/google/protobuf/compiler/rust/accessors/singular_message.cc +++ b/src/google/protobuf/compiler/rust/accessors/singular_message.cc @@ -11,6 +11,7 @@ #include "google/protobuf/compiler/cpp/helpers.h" #include "google/protobuf/compiler/rust/accessors/accessor_case.h" #include "google/protobuf/compiler/rust/accessors/generator.h" +#include "google/protobuf/compiler/rust/accessors/with_presence.h" #include "google/protobuf/compiler/rust/context.h" #include "google/protobuf/compiler/rust/naming.h" #include "google/protobuf/descriptor.h" @@ -22,24 +23,28 @@ namespace rust { void SingularMessage::InMsgImpl(Context& ctx, const FieldDescriptor& field, AccessorCase accessor_case) const { + if (field.has_presence()) { + WithPresenceAccessorsInMsgImpl(ctx, field, accessor_case); + } + // fully qualified message name with modules prefixed std::string msg_type = RsTypePath(ctx, field); std::string field_name = FieldNameWithCollisionAvoidance(field); - ctx.Emit({{"msg_type", msg_type}, - {"field", RsSafeName(field_name)}, - {"raw_field_name", field_name}, - {"view_lifetime", ViewLifetime(accessor_case)}, - {"view_self", ViewReceiver(accessor_case)}, - {"getter_thunk", ThunkName(ctx, field, "get")}, - {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, - {"set_allocated_thunk", ThunkName(ctx, field, "set")}, - { - "getter_body", - [&] { - if (ctx.is_upb()) { - ctx.Emit({}, R"rs( + ctx.Emit( + { + {"msg_type", msg_type}, + {"field", RsSafeName(field_name)}, + {"raw_field_name", field_name}, + {"view_lifetime", ViewLifetime(accessor_case)}, + {"view_self", ViewReceiver(accessor_case)}, + {"getter_thunk", ThunkName(ctx, field, "get")}, + {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}, + {"set_allocated_thunk", ThunkName(ctx, field, "set")}, + { + "getter_body", + [&] { + if (ctx.is_upb()) { + ctx.Emit({}, R"rs( let submsg = unsafe { $getter_thunk$(self.raw_msg()) }; //~ For upb, getters return null if the field is unset, so we need //~ to check for null and return the default instance manually. @@ -50,68 +55,58 @@ void SingularMessage::InMsgImpl(Context& ctx, const FieldDescriptor& field, Some(field) => $msg_type$View::new($pbi$::Private, field), } )rs"); - } else { - ctx.Emit({}, R"rs( + } else { + ctx.Emit({}, R"rs( //~ For C++ kernel, getters automatically return the //~ default_instance if the field is unset. let submsg = unsafe { $getter_thunk$(self.raw_msg()) }; $msg_type$View::new($pbi$::Private, submsg) )rs"); - } - }, - }, - {"getter", - [&] { - ctx.Emit({}, R"rs( + } + }, + }, + {"getter", + [&] { + ctx.Emit({}, R"rs( pub fn $field$($view_self$) -> $msg_type$View<$view_lifetime$> { $getter_body$ } )rs"); - }}, - {"getter_mut_body", - [&] { - if (ctx.is_cpp()) { - ctx.Emit({}, R"rs( + }}, + {"getter_mut_body", + [&] { + if (ctx.is_cpp()) { + ctx.Emit({}, R"rs( let raw_msg = unsafe { $getter_mut_thunk$(self.raw_msg()) }; $msg_type$Mut::from_parent($pbi$::Private, self.as_mutator_message_ref($pbi$::Private), raw_msg) )rs"); - } else { - ctx.Emit({}, R"rs( - let raw_msg = unsafe { + } else { + ctx.Emit({}, R"rs( + let raw_msg = unsafe { $getter_mut_thunk$(self.raw_msg(), self.arena().raw()) }; $msg_type$Mut::from_parent($pbi$::Private, self.as_mutator_message_ref($pbi$::Private), raw_msg) )rs"); - } - }}, - {"getter_mut", - [&] { - if (accessor_case == AccessorCase::VIEW) { - return; - } + } + }}, + {"getter_mut", + [&] { + if (accessor_case == AccessorCase::VIEW) { + return; + } - ctx.Emit({}, R"rs( + ctx.Emit({}, R"rs( pub fn $raw_field_name$_mut(&mut self) -> $msg_type$Mut<'_> { $getter_mut_body$ })rs"); - }}, - {"getter_opt", - [&] { + }}, + {"setter_body", + [&] { + if (accessor_case == AccessorCase::VIEW) return; + if (ctx.is_upb()) { ctx.Emit({}, R"rs( - pub fn $raw_field_name$_opt($view_self$) -> - $pb$::Optional<$msg_type$View<$view_lifetime$>> { - let view = self.$field$(); - $pb$::Optional::new(view, self.has_$raw_field_name$()) - } - )rs"); - }}, - {"setter_body", - [&] { - if (accessor_case == AccessorCase::VIEW) return; - if (ctx.is_upb()) { - ctx.Emit({}, R"rs( // The message and arena are dropped after the setter. The // memory remains allocated as we fuse the arena with the // parent message's arena. @@ -125,8 +120,8 @@ void SingularMessage::InMsgImpl(Context& ctx, const FieldDescriptor& field, msg.as_mutator_message_ref($pbi$::Private).msg()); } )rs"); - } else { - ctx.Emit({}, R"rs( + } else { + ctx.Emit({}, R"rs( // Prevent the memory from being deallocated. The setter // transfers ownership of the memory to the parent message. let mut msg = std::mem::ManuallyDrop::new(val.into_proxied($pbi$::Private)); @@ -135,52 +130,37 @@ void SingularMessage::InMsgImpl(Context& ctx, const FieldDescriptor& field, msg.as_mutator_message_ref($pbi$::Private).msg()); } )rs"); - } - }}, - {"setter", - [&] { - if (accessor_case == AccessorCase::VIEW) return; - ctx.Emit(R"rs( + } + }}, + {"setter", + [&] { + if (accessor_case == AccessorCase::VIEW) return; + ctx.Emit(R"rs( pub fn set_$raw_field_name$(&mut self, val: impl $pb$::IntoProxied<$msg_type$>) { $setter_body$ } )rs"); - }}, - {"hazzer", - [&] { - ctx.Emit({}, R"rs( - pub fn has_$raw_field_name$($view_self$) -> bool { - unsafe { $hazzer_thunk$(self.raw_msg()) } - })rs"); - }}, - {"clearer", - [&] { - if (accessor_case == AccessorCase::VIEW) return; - ctx.Emit({}, R"rs( - pub fn clear_$raw_field_name$(&mut self) { - unsafe { $clearer_thunk$(self.raw_msg()) } - })rs"); - }}}, - R"rs( + }}, + }, + R"rs( $getter$ $getter_mut$ - $getter_opt$ $setter$ - $hazzer$ - $clearer$ )rs"); } void SingularMessage::InExternC(Context& ctx, const FieldDescriptor& field) const { + if (field.has_presence()) { + WithPresenceAccessorsInExternC(ctx, field); + } + ctx.Emit( { {"getter_thunk", ThunkName(ctx, field, "get")}, {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, {"set_allocated_thunk", ThunkName(ctx, field, "set")}, {"getter_mut", [&] { @@ -211,8 +191,6 @@ void SingularMessage::InExternC(Context& ctx, R"rs( fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $ReturnType$; $getter_mut$ - fn $clearer_thunk$(raw_msg: $pbr$::RawMessage); - fn $hazzer_thunk$(raw_msg: $pbr$::RawMessage) -> bool; fn $set_allocated_thunk$(raw_msg: $pbr$::RawMessage, field_msg: $pbr$::RawMessage); )rs"); @@ -220,13 +198,15 @@ void SingularMessage::InExternC(Context& ctx, void SingularMessage::InThunkCc(Context& ctx, const FieldDescriptor& field) const { + if (field.has_presence()) { + WithPresenceAccessorsInThunkCc(ctx, field); + } + ctx.Emit({{"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())}, {"FieldMsg", cpp::QualifiedClassName(field.message_type())}, {"set_allocated_thunk", ThunkName(ctx, field, "set")}, {"getter_thunk", ThunkName(ctx, field, "get")}, {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, {"field", cpp::FieldName(&field)}}, R"cc( const void* $getter_thunk$($QualifiedMsg$* msg) { @@ -235,8 +215,6 @@ void SingularMessage::InThunkCc(Context& ctx, void* $getter_mut_thunk$($QualifiedMsg$* msg) { return static_cast(msg->mutable_$field$()); } - void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); } - bool $hazzer_thunk$($QualifiedMsg$* msg) { return msg->has_$field$(); } void $set_allocated_thunk$($QualifiedMsg$* msg, $FieldMsg$* sub_msg) { msg->set_allocated_$field$(sub_msg); } diff --git a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc index 904f36d94b..8972671ee2 100644 --- a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc +++ b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc @@ -13,6 +13,7 @@ #include "google/protobuf/compiler/rust/accessors/accessor_case.h" #include "google/protobuf/compiler/rust/accessors/default_value.h" #include "google/protobuf/compiler/rust/accessors/generator.h" +#include "google/protobuf/compiler/rust/accessors/with_presence.h" #include "google/protobuf/compiler/rust/context.h" #include "google/protobuf/compiler/rust/naming.h" #include "google/protobuf/compiler/rust/upb_helpers.h" @@ -58,6 +59,10 @@ std::string UpbCTypeNameForFunctions(const FieldDescriptor& field) { void SingularScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field, AccessorCase accessor_case) const { + if (field.has_presence()) { + WithPresenceAccessorsInMsgImpl(ctx, field, accessor_case); + } + std::string field_name = FieldNameWithCollisionAvoidance(field); ctx.Emit( @@ -100,19 +105,6 @@ void SingularScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field, )rs"); } }}, - {"getter_opt", - [&] { - if (!field.has_presence()) return; - ctx.Emit(R"rs( - pub fn $raw_field_name$_opt($view_self$) -> $pb$::Optional<$Scalar$> { - if self.has_$raw_field_name$() { - $pb$::Optional::Set(self.$field$()) - } else { - $pb$::Optional::Unset($default_value$) - } - } - )rs"); - }}, {"setter", [&] { if (accessor_case == AccessorCase::VIEW) return; @@ -140,65 +132,24 @@ void SingularScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field, )rs"); } }}, - {"hazzer", - [&] { - if (!field.has_presence()) return; - if (ctx.is_cpp()) { - ctx.Emit(R"rs( - pub fn has_$raw_field_name$($view_self$) -> bool { - unsafe { $hazzer_thunk$(self.raw_msg()) } - })rs"); - } else { - ctx.Emit(R"rs( - pub fn has_$raw_field_name$($view_self$) -> bool { - unsafe { - let mt = ::mini_table(); - let f = $pbr$::upb_MiniTable_GetFieldByIndex( - mt, $upb_mt_field_index$); - $pbr$::upb_Message_HasBaseField(self.raw_msg(), f) - } - })rs"); - } - }}, - {"clearer", - [&] { - if (accessor_case == AccessorCase::VIEW) return; - if (!field.has_presence()) return; - if (ctx.is_cpp()) { - ctx.Emit(R"rs( - pub fn clear_$raw_field_name$(&mut self) { - unsafe { $clearer_thunk$(self.raw_msg()) } - })rs"); - } else { - ctx.Emit(R"rs( - pub fn clear_$raw_field_name$(&mut self) { - unsafe { - let mt = ::mini_table(); - let f = $pbr$::upb_MiniTable_GetFieldByIndex( - mt, $upb_mt_field_index$); - $pbr$::upb_Message_ClearBaseField(self.raw_msg(), f); - } - })rs"); - } - }}, {"getter_thunk", ThunkName(ctx, field, "get")}, {"setter_thunk", ThunkName(ctx, field, "set")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, }, R"rs( $getter$ - $getter_opt$ $setter$ - $hazzer$ - $clearer$ )rs"); } void SingularScalar::InExternC(Context& ctx, const FieldDescriptor& field) const { - // Only cpp kernel uses thunks. + // Only cpp kernel uses thunks for singular scalars. if (ctx.is_upb()) return; + if (field.has_presence()) { + WithPresenceAccessorsInExternC(ctx, field); + } + // In order to soundly pass a Rust type to C/C++ as a function argument, // the types must be FFI-compatible. // This requires special consideration for enums, which aren't trivial @@ -207,21 +158,9 @@ void SingularScalar::InExternC(Context& ctx, // Upb defines enum thunks as taking `int32_t`, and so we can pass Rust enums // directly to thunks without any cast. ctx.Emit({{"Scalar", RsTypePath(ctx, field)}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, {"getter_thunk", ThunkName(ctx, field, "get")}, - {"setter_thunk", ThunkName(ctx, field, "set")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, - {"with_presence_fields_thunks", - [&] { - if (field.has_presence()) { - ctx.Emit(R"rs( - fn $hazzer_thunk$(raw_msg: $pbr$::RawMessage) -> bool; - fn $clearer_thunk$(raw_msg: $pbr$::RawMessage); - )rs"); - } - }}}, + {"setter_thunk", ThunkName(ctx, field, "set")}}, R"rs( - $with_presence_fields_thunks$ fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $Scalar$; fn $setter_thunk$(raw_msg: $pbr$::RawMessage, val: $Scalar$); )rs"); @@ -229,6 +168,10 @@ void SingularScalar::InExternC(Context& ctx, void SingularScalar::InThunkCc(Context& ctx, const FieldDescriptor& field) const { + if (field.has_presence()) { + WithPresenceAccessorsInThunkCc(ctx, field); + } + std::string scalar; auto enum_ = field.enum_type(); if (enum_ != nullptr) { @@ -245,23 +188,12 @@ void SingularScalar::InThunkCc(Context& ctx, ctx.Emit({{"field", cpp::FieldName(&field)}, {"Scalar", scalar}, {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, {"getter_thunk", ThunkName(ctx, field, "get")}, - {"setter_thunk", ThunkName(ctx, field, "set")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, - {"with_presence_fields_thunks", - [&] { - if (!field.has_presence()) return; - ctx.Emit(R"cc( - bool $hazzer_thunk$($QualifiedMsg$* msg) { - return msg->has_$field$(); - } - void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); } - )cc"); - }}}, + {"setter_thunk", ThunkName(ctx, field, "set")}}, R"cc( - $with_presence_fields_thunks$; - $Scalar$ $getter_thunk$($QualifiedMsg$* msg) { return msg->$field$(); } + $Scalar$ $getter_thunk$($QualifiedMsg$* msg) { + return msg->$field$(); + } void $setter_thunk$($QualifiedMsg$* msg, $Scalar$ val) { msg->set_$field$(val); } diff --git a/src/google/protobuf/compiler/rust/accessors/singular_string.cc b/src/google/protobuf/compiler/rust/accessors/singular_string.cc index 980ffbc9c8..f7a1359e86 100644 --- a/src/google/protobuf/compiler/rust/accessors/singular_string.cc +++ b/src/google/protobuf/compiler/rust/accessors/singular_string.cc @@ -11,6 +11,7 @@ #include "google/protobuf/compiler/cpp/helpers.h" #include "google/protobuf/compiler/rust/accessors/accessor_case.h" #include "google/protobuf/compiler/rust/accessors/generator.h" +#include "google/protobuf/compiler/rust/accessors/with_presence.h" #include "google/protobuf/compiler/rust/context.h" #include "google/protobuf/compiler/rust/naming.h" #include "google/protobuf/descriptor.h" @@ -22,15 +23,17 @@ namespace rust { void SingularString::InMsgImpl(Context& ctx, const FieldDescriptor& field, AccessorCase accessor_case) const { + if (field.has_presence()) { + WithPresenceAccessorsInMsgImpl(ctx, field, accessor_case); + } + std::string field_name = FieldNameWithCollisionAvoidance(field); ctx.Emit( { {"field", RsSafeName(field_name)}, {"raw_field_name", field_name}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, {"getter_thunk", ThunkName(ctx, field, "get")}, {"setter_thunk", ThunkName(ctx, field, "set")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, {"proxied_type", RsTypePath(ctx, field)}, io::Printer::Sub("transform_view", [&] { @@ -54,18 +57,6 @@ void SingularString::InMsgImpl(Context& ctx, const FieldDescriptor& field, $transform_view$ })rs"); }}, - {"getter_opt", - [&] { - if (!field.has_presence()) return; - ctx.Emit(R"rs( - pub fn $raw_field_name$_opt($view_self$) -> $pb$::Optional<$pb$::View<$view_lifetime$, $proxied_type$>> { - $pb$::Optional::new( - self.$field$(), - self.has_$raw_field_name$() - ) - } - )rs"); - }}, {"setter_impl", [&] { if (ctx.is_cpp()) { @@ -109,62 +100,37 @@ void SingularString::InMsgImpl(Context& ctx, const FieldDescriptor& field, } )rs"); }}, - {"hazzer", - [&] { - if (!field.has_presence()) return; - ctx.Emit({}, R"rs( - pub fn has_$raw_field_name$($view_self$) -> bool { - unsafe { $hazzer_thunk$(self.raw_msg()) } - })rs"); - }}, - {"clearer", - [&] { - if (accessor_case == AccessorCase::VIEW) return; - if (!field.has_presence()) return; - ctx.Emit({}, R"rs( - pub fn clear_$raw_field_name$(&mut self) { - unsafe { $clearer_thunk$(self.raw_msg()) } - })rs"); - }}, }, R"rs( $getter$ - $getter_opt$ $setter$ - $hazzer$ - $clearer$ )rs"); } void SingularString::InExternC(Context& ctx, const FieldDescriptor& field) const { - ctx.Emit({{"hazzer_thunk", ThunkName(ctx, field, "has")}, - {"getter_thunk", ThunkName(ctx, field, "get")}, - {"setter_thunk", ThunkName(ctx, field, "set")}, - {"setter", - [&] { - if (ctx.is_cpp()) { - ctx.Emit(R"rs( + if (field.has_presence()) { + WithPresenceAccessorsInExternC(ctx, field); + } + + ctx.Emit( + { + {"getter_thunk", ThunkName(ctx, field, "get")}, + {"setter_thunk", ThunkName(ctx, field, "set")}, + {"setter", + [&] { + if (ctx.is_cpp()) { + ctx.Emit(R"rs( fn $setter_thunk$(raw_msg: $pbr$::RawMessage, val: $pbr$::CppStdString); )rs"); - } else { - ctx.Emit(R"rs( + } else { + ctx.Emit(R"rs( fn $setter_thunk$(raw_msg: $pbr$::RawMessage, val: $pbr$::PtrAndLen); )rs"); - } - }}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, - {"with_presence_fields_thunks", - [&] { - if (field.has_presence()) { - ctx.Emit(R"rs( - fn $hazzer_thunk$(raw_msg: $pbr$::RawMessage) -> bool; - fn $clearer_thunk$(raw_msg: $pbr$::RawMessage); - )rs"); - } - }}}, - R"rs( - $with_presence_fields_thunks$ + } + }}, + }, + R"rs( fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $pbr$::PtrAndLen; $setter$ )rs"); @@ -172,34 +138,27 @@ void SingularString::InExternC(Context& ctx, void SingularString::InThunkCc(Context& ctx, const FieldDescriptor& field) const { - ctx.Emit({{"field", cpp::FieldName(&field)}, - {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())}, - {"hazzer_thunk", ThunkName(ctx, field, "has")}, - {"getter_thunk", ThunkName(ctx, field, "get")}, - {"setter_thunk", ThunkName(ctx, field, "set")}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, - {"with_presence_fields_thunks", - [&] { - if (field.has_presence()) { - ctx.Emit(R"cc( - bool $hazzer_thunk$($QualifiedMsg$* msg) { - return msg->has_$field$(); - } - void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); } - )cc"); - } - }}}, - R"cc( - $with_presence_fields_thunks$; - ::google::protobuf::rust::PtrAndLen $getter_thunk$($QualifiedMsg$* msg) { - absl::string_view val = msg->$field$(); - return ::google::protobuf::rust::PtrAndLen(val.data(), val.size()); - } - void $setter_thunk$($QualifiedMsg$* msg, std::string* s) { - msg->set_$field$(std::move(*s)); - delete s; - } - )cc"); + if (field.has_presence()) { + WithPresenceAccessorsInThunkCc(ctx, field); + } + + ctx.Emit( + { + {"field", cpp::FieldName(&field)}, + {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())}, + {"getter_thunk", ThunkName(ctx, field, "get")}, + {"setter_thunk", ThunkName(ctx, field, "set")}, + }, + R"cc( + ::google::protobuf::rust::PtrAndLen $getter_thunk$($QualifiedMsg$* msg) { + absl::string_view val = msg->$field$(); + return ::google::protobuf::rust::PtrAndLen(val.data(), val.size()); + } + void $setter_thunk$($QualifiedMsg$* msg, std::string* s) { + msg->set_$field$(std::move(*s)); + delete s; + } + )cc"); } } // namespace rust diff --git a/src/google/protobuf/compiler/rust/accessors/with_presence.cc b/src/google/protobuf/compiler/rust/accessors/with_presence.cc new file mode 100644 index 0000000000..fae912d5f5 --- /dev/null +++ b/src/google/protobuf/compiler/rust/accessors/with_presence.cc @@ -0,0 +1,134 @@ +#include "google/protobuf/compiler/rust/accessors/with_presence.h" + +#include + +#include "absl/log/absl_check.h" +#include "google/protobuf/compiler/cpp/helpers.h" +#include "google/protobuf/compiler/rust/accessors/accessor_case.h" +#include "google/protobuf/compiler/rust/context.h" +#include "google/protobuf/compiler/rust/naming.h" +#include "google/protobuf/compiler/rust/upb_helpers.h" +#include "google/protobuf/descriptor.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace rust { + +void WithPresenceAccessorsInMsgImpl(Context& ctx, const FieldDescriptor& field, + AccessorCase accessor_case) { + ABSL_CHECK(field.has_presence()); + + std::string field_name = FieldNameWithCollisionAvoidance(field); + + ctx.Emit( + {{"field", RsSafeName(field_name)}, + {"raw_field_name", field_name}, // Never r# prefixed + {"view_type", RsViewType(ctx, field, ViewLifetime(accessor_case))}, + {"view_self", ViewReceiver(accessor_case)}, + {"hazzer", + [&] { + if (ctx.is_cpp()) { + ctx.Emit({{"hazzer_thunk", ThunkName(ctx, field, "has")}}, + R"rs( + pub fn has_$raw_field_name$($view_self$) -> bool { + unsafe { + $hazzer_thunk$(self.raw_msg()) + } + } + )rs"); + } else { + ctx.Emit({{"upb_mt_field_index", UpbMiniTableFieldIndex(field)}}, + R"rs( + pub fn has_$raw_field_name$($view_self$) -> bool { + unsafe { + let f = $pbr$::upb_MiniTable_GetFieldByIndex( + ::mini_table(), + $upb_mt_field_index$); + $pbr$::upb_Message_HasBaseField(self.raw_msg(), f) + } + } + )rs"); + } + }}, + {"clearer", + [&] { + if (accessor_case == AccessorCase::VIEW) return; + if (ctx.is_cpp()) { + ctx.Emit({{"clearer_thunk", ThunkName(ctx, field, "clear")}}, + R"rs( + pub fn clear_$raw_field_name$(&mut self) { + unsafe { $clearer_thunk$(self.raw_msg()) } + })rs"); + } else { + ctx.Emit({{"upb_mt_field_index", UpbMiniTableFieldIndex(field)}}, + R"rs( + pub fn clear_$raw_field_name$(&mut self) { + unsafe { + let mt = ::mini_table(); + let f = $pbr$::upb_MiniTable_GetFieldByIndex( + mt, $upb_mt_field_index$); + $pbr$::upb_Message_ClearBaseField(self.raw_msg(), f); + } + })rs"); + } + }}, + {"opt_getter", + [&] { + // Cord fields don't support the _opt getter. + if (field.options().ctype() == FieldOptions::CORD) return; + ctx.Emit( + R"rs( + pub fn $raw_field_name$_opt($view_self$) -> $pb$::Optional<$view_type$> { + $pb$::Optional::new(self.$field$(), self.has_$raw_field_name$()) + } + )rs"); + }}}, + R"rs( + $hazzer$ + $clearer$ + $opt_getter$ + )rs"); +} + +void WithPresenceAccessorsInExternC(Context& ctx, + const FieldDescriptor& field) { + ABSL_CHECK(field.has_presence()); + + if (ctx.is_upb()) return; + + ctx.Emit( + { + {"hazzer_thunk", ThunkName(ctx, field, "has")}, + {"clearer_thunk", ThunkName(ctx, field, "clear")}, + }, + R"rs( + fn $hazzer_thunk$(raw_msg: $pbr$::RawMessage) -> bool; + fn $clearer_thunk$(raw_msg: $pbr$::RawMessage); + )rs"); +} + +void WithPresenceAccessorsInThunkCc(Context& ctx, + const FieldDescriptor& field) { + ABSL_CHECK(ctx.is_cpp()); + ABSL_CHECK(field.has_presence()); + + ctx.Emit( + { + {"field", cpp::FieldName(&field)}, + {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())}, + {"hazzer_thunk", ThunkName(ctx, field, "has")}, + {"clearer_thunk", ThunkName(ctx, field, "clear")}, + }, + R"rs( + bool $hazzer_thunk$($QualifiedMsg$* msg) { + return msg->has_$field$(); + } + void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); } + )rs"); +} + +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/rust/accessors/with_presence.h b/src/google/protobuf/compiler/rust/accessors/with_presence.h new file mode 100644 index 0000000000..bd66426734 --- /dev/null +++ b/src/google/protobuf/compiler/rust/accessors/with_presence.h @@ -0,0 +1,28 @@ +#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_WITH_PRESENCE_H__ +#define GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_WITH_PRESENCE_H__ + +#include "google/protobuf/compiler/rust/accessors/accessor_case.h" +#include "google/protobuf/compiler/rust/context.h" +#include "google/protobuf/descriptor.h" + +// Helper functions for generating the common accessors that any with-presence +// field have (the hassers, clearers, and the Optional<> getter). + +namespace google { +namespace protobuf { +namespace compiler { +namespace rust { + +void WithPresenceAccessorsInMsgImpl(Context& ctx, const FieldDescriptor& field, + AccessorCase accessor_case); + +void WithPresenceAccessorsInExternC(Context& ctx, const FieldDescriptor& field); + +void WithPresenceAccessorsInThunkCc(Context& ctx, const FieldDescriptor& field); + +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_WITH_PRESENCE_H__ diff --git a/src/google/protobuf/compiler/rust/naming.cc b/src/google/protobuf/compiler/rust/naming.cc index 5fba572af1..5a907e3866 100644 --- a/src/google/protobuf/compiler/rust/naming.cc +++ b/src/google/protobuf/compiler/rust/naming.cc @@ -16,6 +16,7 @@ #include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" @@ -211,6 +212,39 @@ std::string RsTypePath(Context& ctx, const FieldDescriptor& field) { return GetFullyQualifiedPath(ctx, *field.enum_type()); } ABSL_LOG(FATAL) << "Unsupported field type: " << field.type_name(); + return ""; +} + +std::string RsViewType(Context& ctx, const FieldDescriptor& field, + absl::string_view lifetime) { + switch (GetRustFieldType(field)) { + case RustFieldType::BOOL: + case RustFieldType::INT32: + case RustFieldType::INT64: + case RustFieldType::UINT32: + case RustFieldType::UINT64: + case RustFieldType::FLOAT: + case RustFieldType::DOUBLE: + case RustFieldType::ENUM: + // The View type of all scalars and enums can be spelled as the type + // itself. + return RsTypePath(ctx, field); + case RustFieldType::BYTES: + return absl::StrFormat("&%s [u8]", lifetime); + case RustFieldType::STRING: + return absl::StrFormat("&%s ::__pb::ProtoStr", lifetime); + case RustFieldType::MESSAGE: + if (lifetime.empty()) { + return absl::StrFormat( + "%sView", GetFullyQualifiedPath(ctx, *field.message_type())); + } else { + return absl::StrFormat( + "%sView<%s>", GetFullyQualifiedPath(ctx, *field.message_type()), + lifetime); + } + } + ABSL_LOG(FATAL) << "Unsupported field type: " << field.type_name(); + return ""; } std::string RustModuleForContainingType(Context& ctx, diff --git a/src/google/protobuf/compiler/rust/naming.h b/src/google/protobuf/compiler/rust/naming.h index 4f6632688f..2707d8576a 100644 --- a/src/google/protobuf/compiler/rust/naming.h +++ b/src/google/protobuf/compiler/rust/naming.h @@ -43,6 +43,15 @@ std::string RawMapThunk(Context& ctx, const EnumDescriptor& desc, // It may be crate-relative, or directly reference the owning crate of the type. std::string RsTypePath(Context& ctx, const FieldDescriptor& field); +// Returns the 'simple spelling' of the Rust View type for the provided field. +// For example, `i32` for int32 fields and `SomeMsgView<'$lifetime$>` for +// message fields, or `SomeMsgView` if an empty lifetime is provided). +// +// The returned type will always be functionally substitutable for the +// corresponding View<'$lifetime$, $sometype$> of the field's Rust type. +std::string RsViewType(Context& ctx, const FieldDescriptor& field, + absl::string_view lifetime); + std::string EnumRsName(const EnumDescriptor& desc); std::string EnumValueRsName(const EnumValueDescriptor& value);