diff --git a/rust/BUILD b/rust/BUILD index bc7700a207..68c6d801a7 100644 --- a/rust/BUILD +++ b/rust/BUILD @@ -62,6 +62,7 @@ PROTOBUF_SHARED = [ "proxied.rs", "repeated.rs", "shared.rs", + "singular.rs", "string.rs", # go/keep-sorted end ] diff --git a/rust/cpp.rs b/rust/cpp.rs index d1bb3f7ad3..af89f31558 100644 --- a/rust/cpp.rs +++ b/rust/cpp.rs @@ -11,8 +11,8 @@ use crate::__internal::{Enum, MatcherEq, Private, SealedInternal}; use crate::{ AsMut, AsView, Clear, ClearAndParse, CopyFrom, IntoProxied, Map, MapIter, MapKey, MapMut, MapValue, MapView, MergeFrom, Message, MessageMutInterop, Mut, MutProxied, ParseError, - ProtoBytes, ProtoStr, ProtoString, Proxied, ProxiedInRepeated, Repeated, RepeatedMut, - RepeatedView, Serialize, SerializeError, TakeFrom, View, + ProtoBytes, ProtoStr, ProtoString, Proxied, Repeated, RepeatedMut, RepeatedView, Serialize, + SerializeError, Singular, TakeFrom, View, }; use core::fmt::Debug; use paste::paste; @@ -549,7 +549,7 @@ impl CppTypeConversions for ProtoBytes { } } -unsafe impl ProxiedInRepeated for T +unsafe impl Singular for T where Self: MutProxied + CppGetRawMessage + Message, for<'a> View<'a, Self>: @@ -695,7 +695,7 @@ macro_rules! impl_repeated_primitives { additional: usize); } - unsafe impl ProxiedInRepeated for $t { + unsafe impl Singular for $t { #[allow(dead_code)] #[inline] fn repeated_new(_: Private) -> Repeated<$t> { @@ -786,7 +786,7 @@ unsafe extern "C" { } /// Cast a `RepeatedView` to `RepeatedView`. -pub fn cast_enum_repeated_view( +pub fn cast_enum_repeated_view( repeated: RepeatedView, ) -> RepeatedView { // SAFETY: the implementer of `Enum` has promised that this @@ -798,7 +798,7 @@ pub fn cast_enum_repeated_view( /// /// Writing an unknown value is sound because all enums /// are representationally open. -pub fn cast_enum_repeated_mut( +pub fn cast_enum_repeated_mut( mut repeated: RepeatedMut, ) -> RepeatedMut { // SAFETY: the implementer of `Enum` has promised that this @@ -813,15 +813,12 @@ pub fn cast_enum_repeated_mut( /// Cast a `RepeatedMut` to `RepeatedMut` and call /// repeated_reserve. -pub fn reserve_enum_repeated_mut( - repeated: RepeatedMut, - additional: usize, -) { +pub fn reserve_enum_repeated_mut(repeated: RepeatedMut, additional: usize) { let int_repeated = cast_enum_repeated_mut(repeated); - ProxiedInRepeated::repeated_reserve(Private, int_repeated, additional); + Singular::repeated_reserve(Private, int_repeated, additional); } -pub fn new_enum_repeated() -> Repeated { +pub fn new_enum_repeated() -> Repeated { let int_repeated = Repeated::::new(); let raw = int_repeated.inner.raw(); std::mem::forget(int_repeated); @@ -833,11 +830,11 @@ pub fn new_enum_repeated() -> Repeated { /// # Safety /// - The passed in `&mut Repeated` must not be used after this function is /// called. -pub unsafe fn free_enum_repeated(repeated: &mut Repeated) { +pub unsafe fn free_enum_repeated(repeated: &mut Repeated) { unsafe { let mut int_r: Repeated = Repeated::from_inner(Private, InnerRepeated::from_raw(repeated.inner.raw())); - ProxiedInRepeated::repeated_free(Private, &mut int_r); + Singular::repeated_free(Private, &mut int_r); std::mem::forget(int_r); } } @@ -1273,7 +1270,7 @@ generate_map_key_impl!( impl MapValue for Value where Key: MapKey + FfiMapKey + CppMapTypeConversions, - Value: Proxied + CppMapTypeConversions, + Value: Singular + CppMapTypeConversions, { fn map_new(_private: Private) -> Map { unsafe { diff --git a/rust/internal.rs b/rust/internal.rs index eab80bcabd..684a7327f7 100644 --- a/rust/internal.rs +++ b/rust/internal.rs @@ -14,7 +14,8 @@ pub use paste::paste; use crate::map; pub use crate::r#enum::Enum; -use crate::repeated; +use crate::repeated::RepeatedView; +use crate::singular::Singular; use crate::MapKey; pub use crate::ProtoStr; pub use std::fmt::Debug; @@ -47,10 +48,7 @@ pub trait MatcherEq: SealedInternal + Debug { } /// Used by the proto! macro to get a default value for a repeated field. -pub fn get_repeated_default_value( - _: Private, - _: repeated::RepeatedView<'_, T>, -) -> T { +pub fn get_repeated_default_value(_: Private, _: RepeatedView<'_, T>) -> T { Default::default() } diff --git a/rust/map.rs b/rust/map.rs index 1745c38734..d9ec929315 100644 --- a/rust/map.rs +++ b/rust/map.rs @@ -6,7 +6,8 @@ // https://developers.google.com/open-source/licenses/bsd use crate::{ - AsMut, AsView, IntoMut, IntoProxied, IntoView, Message, Mut, MutProxied, Proxied, View, + AsMut, AsView, IntoMut, IntoProxied, IntoView, Message, Mut, MutProxied, Proxied, Singular, + View, __internal::runtime::{InnerMap, InnerMapMut, RawMap, RawMapIter}, __internal::{Private, SealedInternal}, }; @@ -84,9 +85,11 @@ impl> Drop for Map { /// A trait implemented by types which are allowed as keys in maps. /// This is all types for fields except for repeated, maps, bytes, messages and enums. -pub trait MapKey: Proxied {} +pub trait MapKey: Proxied + SealedInternal {} -pub trait MapValue: Proxied { +/// A trait implemented by types which are allowed as values in maps, which is all Singular types. +/// This trait is distinct from `Singular` only because of the generic `K: MapKey`. +pub trait MapValue: Singular + SealedInternal { #[doc(hidden)] fn map_new(_private: Private) -> Map; diff --git a/rust/primitive.rs b/rust/primitive.rs index 30a432fa53..dddf4fd81d 100644 --- a/rust/primitive.rs +++ b/rust/primitive.rs @@ -34,7 +34,7 @@ macro_rules! impl_singular_primitives { } } - // ProxiedInRepeated is implemented in {cpp,upb}.rs + // Singular is implemented in {cpp,upb}.rs )* } } diff --git a/rust/repeated.rs b/rust/repeated.rs index 3244408502..d31f4e8f88 100644 --- a/rust/repeated.rs +++ b/rust/repeated.rs @@ -15,7 +15,8 @@ use std::iter::FusedIterator; use std::marker::PhantomData; use crate::{ - AsMut, AsView, IntoMut, IntoProxied, IntoView, Message, Mut, MutProxied, Proxied, View, + AsMut, AsView, IntoMut, IntoProxied, IntoView, Message, Mut, MutProxied, Proxied, Singular, + View, __internal::runtime::{InnerRepeated, InnerRepeatedMut, RawRepeatedField}, __internal::{Private, SealedInternal}, }; @@ -77,7 +78,7 @@ impl<'msg, T> RepeatedView<'msg, T> { impl<'msg, T> RepeatedView<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { /// Gets the length of the repeated field. #[inline] @@ -140,7 +141,7 @@ impl<'msg, T> RepeatedMut<'msg, T> { impl<'msg, T> RepeatedMut<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { /// Gets the length of the repeated field. #[inline] @@ -248,7 +249,7 @@ where impl Repeated where - T: ProxiedInRepeated, + T: Singular, { pub fn as_view(&self) -> View<'_, Repeated> { RepeatedView { raw: self.inner.raw(), _phantom: PhantomData } @@ -262,7 +263,7 @@ where impl<'msg, T> IntoProxied> for RepeatedView<'msg, T> where - T: 'msg + ProxiedInRepeated, + T: 'msg + Singular, { fn into_proxied(self, _private: Private) -> Repeated { let mut repeated: Repeated = Repeated::new(); @@ -273,7 +274,7 @@ where impl<'msg, T> IntoProxied> for RepeatedMut<'msg, T> where - T: 'msg + ProxiedInRepeated, + T: 'msg + Singular, { fn into_proxied(self, _private: Private) -> Repeated { IntoProxied::into_proxied(self.as_view(), _private) @@ -283,7 +284,7 @@ where impl<'msg, T, I, U> IntoProxied> for I where I: Iterator, - T: 'msg + ProxiedInRepeated, + T: 'msg + Singular, U: IntoProxied, { fn into_proxied(self, _private: Private) -> Repeated { @@ -293,84 +294,6 @@ where } } -/// Types that can appear in a `Repeated`. -/// -/// This trait is implemented by generated code to communicate how the proxied -/// type can be manipulated for a repeated field. -/// -/// Scalars and messages implement `ProxiedInRepeated`. -/// -/// # Safety -/// - It must be sound to call `*_unchecked*(x)` with an `index` less than -/// `repeated_len(x)`. -pub unsafe trait ProxiedInRepeated: Proxied + SealedInternal { - /// Constructs a new owned `Repeated` field. - #[doc(hidden)] - fn repeated_new(_private: Private) -> Repeated; - - /// Frees the repeated field in-place, for use in `Drop`. - /// - /// # Safety - /// - After `repeated_free`, no other methods on the input are safe to call. - #[doc(hidden)] - unsafe fn repeated_free(_private: Private, _repeated: &mut Repeated); - - /// Gets the length of the repeated field. - #[doc(hidden)] - fn repeated_len(_private: Private, repeated: View>) -> usize; - - /// Appends a new element to the end of the repeated field. - #[doc(hidden)] - fn repeated_push(_private: Private, repeated: Mut>, val: impl IntoProxied); - - /// Clears the repeated field of elements. - #[doc(hidden)] - fn repeated_clear(_private: Private, repeated: Mut>); - - /// # Safety - /// `index` must be less than `Self::repeated_len(repeated)` - #[doc(hidden)] - unsafe fn repeated_get_unchecked( - _private: Private, - repeated: View>, - index: usize, - ) -> View; - - /// # Safety - /// `index` must be less than `Self::repeated_len(repeated)` - #[allow(unused_variables)] - #[doc(hidden)] - unsafe fn repeated_get_mut_unchecked( - _private: Private, - repeated: Mut>, - index: usize, - ) -> Mut - where - Self: Message, - { - panic!("repeated_get_mut_unchecked is only implemented for messages"); - } - - /// # Safety - /// `index` must be less than `Self::repeated_len(repeated)` - #[doc(hidden)] - unsafe fn repeated_set_unchecked( - _private: Private, - repeated: Mut>, - index: usize, - val: impl IntoProxied, - ); - - /// Copies the values in the `src` repeated field into `dest`. - #[doc(hidden)] - fn repeated_copy_from(_private: Private, src: View>, dest: Mut>); - - /// Ensures that the repeated field has enough space allocated to insert at - /// least `additional` values without an allocation. - #[doc(hidden)] - fn repeated_reserve(_private: Private, repeated: Mut>, additional: usize); -} - /// An iterator over the values inside of a [`View>`](RepeatedView). #[derive(Clone)] pub struct RepeatedIter<'msg, T> { @@ -391,19 +314,19 @@ impl<'msg, T> Debug for RepeatedIter<'msg, T> { /// /// Users will generally write [`View>`](RepeatedView) or /// [`Mut>`](RepeatedMut) to access the repeated elements -pub struct Repeated { +pub struct Repeated { pub(crate) inner: InnerRepeated, _phantom: PhantomData, } // SAFETY: `Repeated` is Sync because it does not implement interior mutability. -unsafe impl Sync for Repeated {} +unsafe impl Sync for Repeated {} // SAFETY: `Repeated` is Send because it's not bound to a specific thread e.g. // it does not use thread-local data or similar. -unsafe impl Send for Repeated {} +unsafe impl Send for Repeated {} -impl Repeated { +impl Repeated { pub fn new() -> Self { T::repeated_new(Private) } @@ -417,13 +340,13 @@ impl Repeated { } } -impl Default for Repeated { +impl Default for Repeated { fn default() -> Self { Repeated::new() } } -impl Drop for Repeated { +impl Drop for Repeated { fn drop(&mut self) { // SAFETY: only called once unsafe { T::repeated_free(Private, self) } @@ -432,7 +355,7 @@ impl Drop for Repeated { impl Proxied for Repeated where - T: ProxiedInRepeated, + T: Singular, { type View<'msg> = RepeatedView<'msg, T> @@ -440,11 +363,11 @@ where Repeated: 'msg; } -impl SealedInternal for Repeated where T: ProxiedInRepeated {} +impl SealedInternal for Repeated where T: Singular {} impl AsView for Repeated where - T: ProxiedInRepeated, + T: Singular, { type Proxied = Self; @@ -455,7 +378,7 @@ where impl MutProxied for Repeated where - T: ProxiedInRepeated, + T: Singular, { type Mut<'msg> = RepeatedMut<'msg, T> @@ -465,7 +388,7 @@ where impl AsMut for Repeated where - T: ProxiedInRepeated, + T: Singular, { type MutProxied = Self; @@ -474,11 +397,11 @@ where } } -impl<'msg, T> SealedInternal for RepeatedView<'msg, T> where T: ProxiedInRepeated + 'msg {} +impl<'msg, T> SealedInternal for RepeatedView<'msg, T> where T: Singular + 'msg {} impl<'msg, T> AsView for RepeatedView<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { type Proxied = Repeated; @@ -490,7 +413,7 @@ where impl<'msg, T> IntoView<'msg> for RepeatedView<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { #[inline] fn into_view<'shorter>(self) -> View<'shorter, Self::Proxied> @@ -501,11 +424,11 @@ where } } -impl<'msg, T> SealedInternal for RepeatedMut<'msg, T> where T: ProxiedInRepeated + 'msg {} +impl<'msg, T> SealedInternal for RepeatedMut<'msg, T> where T: Singular + 'msg {} impl<'msg, T> AsView for RepeatedMut<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { type Proxied = Repeated; @@ -517,7 +440,7 @@ where impl<'msg, T> IntoView<'msg> for RepeatedMut<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { #[inline] fn into_view<'shorter>(self) -> RepeatedView<'shorter, T> @@ -530,7 +453,7 @@ where impl<'msg, T> AsMut for RepeatedMut<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { type MutProxied = Repeated; @@ -542,7 +465,7 @@ where impl<'msg, T> IntoMut<'msg> for RepeatedMut<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { #[inline] fn into_mut<'shorter>(self) -> RepeatedMut<'shorter, T> @@ -555,7 +478,7 @@ where impl<'msg, T> iter::Iterator for RepeatedIter<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { type Item = View<'msg, T>; @@ -574,18 +497,18 @@ where } } -impl<'msg, T: ProxiedInRepeated> ExactSizeIterator for RepeatedIter<'msg, T> { +impl<'msg, T: Singular> ExactSizeIterator for RepeatedIter<'msg, T> { fn len(&self) -> usize { self.view.len() - self.current_index } } // TODO: impl DoubleEndedIterator -impl<'msg, T: ProxiedInRepeated> FusedIterator for RepeatedIter<'msg, T> {} +impl<'msg, T: Singular> FusedIterator for RepeatedIter<'msg, T> {} impl<'msg, T> iter::IntoIterator for RepeatedView<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { type Item = View<'msg, T>; type IntoIter = RepeatedIter<'msg, T>; @@ -597,7 +520,7 @@ where impl<'msg, T> iter::IntoIterator for &'_ RepeatedView<'msg, T> where - T: ProxiedInRepeated + 'msg, + T: Singular + 'msg, { type Item = View<'msg, T>; type IntoIter = RepeatedIter<'msg, T>; @@ -609,7 +532,7 @@ where impl<'borrow, T> iter::IntoIterator for &'borrow RepeatedMut<'_, T> where - T: ProxiedInRepeated + 'borrow, + T: Singular + 'borrow, { type Item = View<'borrow, T>; type IntoIter = RepeatedIter<'borrow, T>; @@ -621,7 +544,7 @@ where impl<'msg, 'view, T, ViewT> Extend for RepeatedMut<'msg, T> where - T: ProxiedInRepeated + 'view, + T: Singular + 'view, ViewT: IntoProxied, { fn extend>(&mut self, iter: I) { diff --git a/rust/shared.rs b/rust/shared.rs index cfd20eb93c..8d8c8e0f06 100644 --- a/rust/shared.rs +++ b/rust/shared.rs @@ -27,7 +27,7 @@ pub use crate::codegen_traits::{ pub use crate::cord::{ProtoBytesCow, ProtoStringCow}; pub use crate::map::{Map, MapIter, MapKey, MapMut, MapValue, MapView}; -// TODO: Remove this alias once we have confirmed theres no incoming references. +// TODO: Remove this alias once we have confirmed there are no incoming references. pub use MapValue as ProxiedInMapValue; pub use crate::optional::Optional; @@ -35,10 +35,14 @@ pub use crate::proxied::{ AsMut, AsView, IntoMut, IntoProxied, IntoView, Mut, MutProxied, Proxied, View, }; pub use crate::r#enum::{Enum, UnknownEnumValue}; -pub use crate::repeated::{ProxiedInRepeated, Repeated, RepeatedIter, RepeatedMut, RepeatedView}; +pub use crate::repeated::{Repeated, RepeatedIter, RepeatedMut, RepeatedView}; +pub use crate::singular::Singular; pub use crate::string::{ProtoBytes, ProtoStr, ProtoString, Utf8Error}; pub use protobuf_macros::proto_proc as proto; +// TODO: Remove this alias once we have confirmed there are no incoming references. +pub use Singular as ProxiedInRepeated; + pub mod prelude; /// The `__internal` module is for necessary encapsulation breaks between @@ -68,6 +72,7 @@ mod optional; mod primitive; mod proxied; mod repeated; +mod singular; mod string; #[cfg(not(bzl))] diff --git a/rust/singular.rs b/rust/singular.rs new file mode 100644 index 0000000000..c73d38b80c --- /dev/null +++ b/rust/singular.rs @@ -0,0 +1,93 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2026 Google LLC. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +use crate::{ + IntoProxied, Message, Mut, Proxied, Repeated, View, + __internal::{Private, SealedInternal}, +}; + +/// Singular types are types which are allowed as a simple field, or in a repeated, or as a map +/// value. +/// +/// In typical Protobuf terminology, 'singular' refers to a property of individual field (namely +/// that it is not a repeated or map field), but in this case this Singular trait is implemented +/// for any type which is usable in that position, which is also the same types usable as a repeated +/// or map value. +/// +/// Note that a subset of Singular types are permitted as Map keys: messages, bytes and enums are +/// not allowed in that position. +/// +/// # Safety +/// - It must be sound to call `*_unchecked*(x)` with an `index` less than +/// `repeated_len(x)`. +pub unsafe trait Singular: Proxied + SealedInternal { + /// Constructs a new owned `Repeated` field. + #[doc(hidden)] + fn repeated_new(_private: Private) -> Repeated; + + /// Frees the repeated field in-place, for use in `Drop`. + /// + /// # Safety + /// - After `repeated_free`, no other methods on the input are safe to call. + #[doc(hidden)] + unsafe fn repeated_free(_private: Private, _repeated: &mut Repeated); + + /// Gets the length of the repeated field. + #[doc(hidden)] + fn repeated_len(_private: Private, repeated: View>) -> usize; + + /// Appends a new element to the end of the repeated field. + #[doc(hidden)] + fn repeated_push(_private: Private, repeated: Mut>, val: impl IntoProxied); + + /// Clears the repeated field of elements. + #[doc(hidden)] + fn repeated_clear(_private: Private, repeated: Mut>); + + /// # Safety + /// `index` must be less than `Self::repeated_len(repeated)` + #[doc(hidden)] + unsafe fn repeated_get_unchecked( + _private: Private, + repeated: View>, + index: usize, + ) -> View; + + /// # Safety + /// `index` must be less than `Self::repeated_len(repeated)` + #[allow(unused_variables)] + #[doc(hidden)] + unsafe fn repeated_get_mut_unchecked( + _private: Private, + repeated: Mut>, + index: usize, + ) -> Mut + where + Self: Message, + { + panic!("repeated_get_mut_unchecked is only implemented for messages"); + } + + /// # Safety + /// `index` must be less than `Self::repeated_len(repeated)` + #[doc(hidden)] + unsafe fn repeated_set_unchecked( + _private: Private, + repeated: Mut>, + index: usize, + val: impl IntoProxied, + ); + + /// Copies the values in the `src` repeated field into `dest`. + #[doc(hidden)] + fn repeated_copy_from(_private: Private, src: View>, dest: Mut>); + + /// Ensures that the repeated field has enough space allocated to insert at + /// least `additional` values without an allocation. + #[doc(hidden)] + fn repeated_reserve(_private: Private, repeated: Mut>, additional: usize); +} diff --git a/rust/upb.rs b/rust/upb.rs index fb0fdf9418..49b7e5046e 100644 --- a/rust/upb.rs +++ b/rust/upb.rs @@ -12,8 +12,8 @@ use crate::{ AsMut, AsView, Clear, ClearAndParse, CopyFrom, IntoProxied, Map, MapIter, MapKey, MapMut, MapValue, MapView, MergeFrom, Message, MessageMut, MessageMutInterop, MessageView, MessageViewInterop, Mut, OwnedMessageInterop, ParseError, ProtoBytes, ProtoStr, ProtoString, - Proxied, ProxiedInRepeated, Repeated, RepeatedMut, RepeatedView, Serialize, SerializeError, - TakeFrom, View, + Proxied, Repeated, RepeatedMut, RepeatedView, Serialize, SerializeError, Singular, TakeFrom, + View, }; use std::fmt::Debug; use std::marker::PhantomData; @@ -393,7 +393,7 @@ impl<'msg> InnerRepeatedMut<'msg> { } } -unsafe impl ProxiedInRepeated for T +unsafe impl Singular for T where T: EntityType + UpbTypeConversions, { @@ -534,7 +534,7 @@ impl<'msg, T> RepeatedMut<'msg, T> { } /// Returns a static empty RepeatedView. -pub fn empty_array() -> RepeatedView<'static, T> { +pub fn empty_array() -> RepeatedView<'static, T> { // TODO: Consider creating a static empty array in C. // Use `i32` for a shared empty repeated for all repeated types in the program. @@ -1362,11 +1362,7 @@ pub unsafe fn message_set_bytes_field<'msg, P: Message + AssociatedMiniTable>( /// # Safety /// - The field at `index` must be a repeated field of `T`. -pub unsafe fn message_set_repeated_field< - 'msg, - P: Message + AssociatedMiniTable, - T: ProxiedInRepeated, ->( +pub unsafe fn message_set_repeated_field<'msg, P: Message + AssociatedMiniTable, T: Singular>( parent: MessageMutInner<'msg, P>, index: u32, val: impl IntoProxied>, diff --git a/src/google/protobuf/compiler/rust/enum.cc b/src/google/protobuf/compiler/rust/enum.cc index b2a8c7411f..4f964a0f79 100644 --- a/src/google/protobuf/compiler/rust/enum.cc +++ b/src/google/protobuf/compiler/rust/enum.cc @@ -311,7 +311,7 @@ void GenerateEnumDefinition(Context& ctx, const EnumDescriptor& desc, {"name", name}, }, R"rs( - unsafe impl $pb$::ProxiedInRepeated for $name$ { + unsafe impl $pb$::Singular for $name$ { fn repeated_new(_private: $pbi$::Private) -> $pb$::Repeated { $pbr$::new_enum_repeated() }