Implement the rest of the v0.6 Optional design

This introduces a new OptionalProxied trait in order to support the needed trait methods.

PiperOrigin-RevId: 546938819
pull/13265/head
Protobuf Team Bot 1 year ago committed by Copybara-Service
parent 54377be12c
commit 0d7a39663b
  1. 462
      rust/optional.rs
  2. 2
      rust/protobuf.rs
  3. 65
      rust/proxied.rs
  4. 2
      rust/shared.rs

@ -33,12 +33,11 @@
#![allow(unused)]
use crate::__internal::Private;
use crate::{Mut, MutProxy, Proxied, SettableValue, View, ViewProxy};
use crate::{Mut, MutProxy, Proxied, ProxiedWithPresence, SettableValue, View, ViewProxy};
use std::convert::{AsMut, AsRef};
use std::fmt::{self, Debug};
/// The type that will go here is not yet defined.
pub type Todo<T = ()> = (std::marker::PhantomData<T>, std::convert::Infallible);
use std::panic;
use std::ptr;
/// A protobuf value from a field that may not be set.
///
@ -51,13 +50,15 @@ pub type Todo<T = ()> = (std::marker::PhantomData<T>, std::convert::Infallible);
/// Two `Optional`s are equal if they match both presence and the field values.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Optional<SetVal, UnsetVal = SetVal> {
/// The field is present. It can be accessed through this `T`.
/// The field is set; it is present in the serialized message.
///
/// - For an `_opt()` accessor, this contains a `View<impl Proxied>`.
/// - For a `_mut()` accessor, this contains a [`PresentField`].
/// - For a `_mut()` accessor, this contains a [`PresentField`] that can be
/// used to access the current value, convert to [`Mut`], clear presence,
/// or set a new value.
Set(SetVal),
/// The field is unset.
/// The field is unset; it is absent in the serialized message.
///
/// - For an `_opt()` accessor, this contains a `View<impl Proxied>` with
/// the default value.
@ -73,6 +74,11 @@ impl<T> Optional<T> {
Optional::Set(x) | Optional::Unset(x) => x,
}
}
/// Constructs an `Optional<T>` with a `T` value and presence bit.
pub fn new(val: T, is_set: bool) -> Self {
if is_set { Optional::Set(val) } else { Optional::Unset(val) }
}
}
impl<T, A> Optional<T, A> {
@ -105,7 +111,7 @@ pub type FieldEntry<'a, T> = Optional<PresentField<'a, T>, AbsentField<'a, T>>;
/// Methods for `_mut()` accessors of optional types.
///
/// The most common methods are [`set`] and [`or_default`].
impl<'msg, T: Proxied + ?Sized + 'msg> FieldEntry<'msg, T> {
impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> FieldEntry<'msg, T> {
// is_set() is provided by `impl<T, A> Optional<T, A>`
/// Gets a mutator for this field. Sets to the default value if not set.
@ -118,30 +124,27 @@ impl<'msg, T: Proxied + ?Sized + 'msg> FieldEntry<'msg, T> {
/// Sets the value of this field to `val`.
///
/// Equivalent to `self.or_default().set(val)`.
/// Equivalent to `self.or_default().set(val)`, but does not consume `self`.
pub fn set(&mut self, val: impl SettableValue<T>) {
match self {
Optional::Set(x) => x.set(val),
Optional::Unset(x) => todo!(),
}
transform_mut(self, |mut self_| match self_ {
Optional::Set(ref mut present) => {
present.set(val);
self_
}
Optional::Unset(absent) => Optional::Set(absent.set(val)),
})
}
/// Clears the field; `is_set()` will return `false`.
pub fn clear(&mut self) {
todo!("b/285308646: Requires a trait method")
}
/// Gets an immutable view of this field, using its default value if not
/// set.
///
/// This has a shorter lifetime than the `field_name()` message accessor;
/// `into_view` provides that lifetime.
pub fn get(self) -> View<'msg, T> {
self.into_view()
transform_mut(self, |self_| match self_ {
Optional::Set(present) => Optional::Unset(present.clear()),
absent => absent,
})
}
}
impl<'msg, T: Proxied + ?Sized + 'msg> ViewProxy<'msg> for FieldEntry<'msg, T> {
impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> ViewProxy<'msg> for FieldEntry<'msg, T> {
type Proxied = T;
fn as_view(&self) -> View<'_, T> {
@ -169,20 +172,21 @@ impl<'msg, T: Proxied + ?Sized + 'msg> ViewProxy<'msg> for FieldEntry<'msg, T> {
/// set field.
pub struct PresentField<'msg, T>
where
T: Proxied + ?Sized + 'msg,
T: ProxiedWithPresence + ?Sized + 'msg,
{
inner: Todo<Mut<'msg, T>>,
inner: T::PresentMutData<'msg>,
}
impl<'msg, T: Proxied + ?Sized + 'msg> Debug for PresentField<'msg, T> {
impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> Debug for PresentField<'msg, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
todo!()
self.inner.fmt(f)
}
}
impl<'msg, T: Proxied + ?Sized + 'msg> PresentField<'msg, T> {
pub fn get(self) -> View<'msg, T> {
self.into_view()
impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> PresentField<'msg, T> {
#[doc(hidden)]
pub fn from_inner(_private: Private, inner: T::PresentMutData<'msg>) -> Self {
Self { inner }
}
pub fn set(&mut self, val: impl SettableValue<T>) {
@ -190,8 +194,8 @@ impl<'msg, T: Proxied + ?Sized + 'msg> PresentField<'msg, T> {
}
/// See [`FieldEntry::clear`].
pub fn clear(self) -> AbsentField<'msg, T> {
todo!("b/285308646: Requires a trait method")
pub fn clear(mut self) -> AbsentField<'msg, T> {
AbsentField { inner: T::clear_present_field(self.inner) }
}
// This cannot provide `reborrow` - `clear` consumes after setting the field
@ -200,35 +204,35 @@ impl<'msg, T: Proxied + ?Sized + 'msg> PresentField<'msg, T> {
impl<'msg, T> ViewProxy<'msg> for PresentField<'msg, T>
where
T: Proxied + ?Sized + 'msg,
T: ProxiedWithPresence + ?Sized + 'msg,
{
type Proxied = T;
fn as_view(&self) -> View<'_, T> {
todo!("b/285308646: Requires a trait method")
self.inner.as_view()
}
fn into_view<'shorter>(self) -> View<'shorter, T>
where
'msg: 'shorter,
{
todo!("b/285308646: Requires a trait method")
self.inner.into_view()
}
}
impl<'msg, T> MutProxy<'msg> for PresentField<'msg, T>
where
T: Proxied + ?Sized + 'msg,
T: ProxiedWithPresence + ?Sized + 'msg,
{
fn as_mut(&mut self) -> Mut<'_, T> {
todo!("b/285308646: Requires a trait method")
self.inner.as_mut()
}
fn into_mut<'shorter>(self) -> Mut<'shorter, T>
where
'msg: 'shorter,
{
todo!("b/285308646: Requires a trait method")
self.inner.into_mut()
}
}
@ -236,18 +240,23 @@ where
/// non-set field.
pub struct AbsentField<'a, T>
where
T: Proxied + ?Sized + 'a,
T: ProxiedWithPresence + ?Sized + 'a,
{
inner: Todo<Option<Mut<'a, T>>>,
inner: T::AbsentMutData<'a>,
}
impl<'msg, T: Proxied + ?Sized + 'msg> Debug for AbsentField<'msg, T> {
impl<'msg, T: ProxiedWithPresence + ?Sized + 'msg> Debug for AbsentField<'msg, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
todo!()
self.inner.fmt(f)
}
}
impl<'msg, T: Proxied + ?Sized> AbsentField<'msg, T> {
impl<'msg, T: ProxiedWithPresence + ?Sized> AbsentField<'msg, T> {
#[doc(hidden)]
pub fn from_inner(_private: Private, inner: T::AbsentMutData<'msg>) -> Self {
Self { inner }
}
/// Gets the default value for this unset field.
///
/// This is the same value that the primitive accessor would provide.
@ -258,12 +267,12 @@ impl<'msg, T: Proxied + ?Sized> AbsentField<'msg, T> {
/// See [`FieldEntry::set`]. Note that this consumes and returns a
/// `PresentField`.
pub fn set(self, val: impl SettableValue<T>) -> PresentField<'msg, T> {
todo!("b/285308646: Requires a trait method")
PresentField { inner: val.set_on_absent(Private, self.inner) }
}
/// Sets this absent field to its default value.
pub fn set_default(self) -> PresentField<'msg, T> {
todo!("b/285308646: Requires a trait method")
PresentField { inner: T::set_absent_to_default(self.inner) }
}
// This cannot provide `reborrow` - `set` consumes after setting the field
@ -273,18 +282,373 @@ impl<'msg, T: Proxied + ?Sized> AbsentField<'msg, T> {
impl<'msg, T> ViewProxy<'msg> for AbsentField<'msg, T>
where
T: Proxied + ?Sized + 'msg,
T: ProxiedWithPresence + ?Sized + 'msg,
{
type Proxied = T;
fn as_view(&self) -> View<'_, T> {
todo!("b/285308646: Requires a trait method")
self.inner.as_view()
}
fn into_view<'shorter>(self) -> View<'shorter, T>
where
'msg: 'shorter,
{
todo!("b/285308646: Requires a trait method")
self.inner.into_view()
}
}
/// Transforms a mutable reference in-place, treating it as if it were owned.
///
/// The program will abort if `transform` panics.
///
/// This is the same operation as provided by [`take_mut::take`].
///
/// [`take_mut::take`]: https://docs.rs/take_mut/latest/take_mut/fn.take.html
fn transform_mut<T>(mut_ref: &mut T, transform: impl FnOnce(T) -> T) {
#[cold]
#[inline(never)]
fn panicked_in_transform_mut() -> ! {
use std::io::Write as _;
let backtrace = std::backtrace::Backtrace::force_capture();
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
let _ = write!(&mut stderr, "BUG: A protobuf mutator panicked! Backtrace:\n{backtrace}\n");
let _ = stderr.flush();
std::process::abort()
}
// https://play.rust-lang.org/?edition=2021&gist=f3014e1f209013f0a38352e211f4a240
// provides a sample test to confirm this operation is sound in Miri.
// SAFETY:
// - `old_t` is not dropped without also replacing `*mut_ref`, preventing a
// double-free.
// - If `transform` panics, the process aborts since `*mut_ref` has no possible
// valid value.
// - After `ptr::write`, a valid `T` is located at `*mut_ref`
unsafe {
let p: *mut T = mut_ref;
let old_t = p.read();
let new_t = panic::catch_unwind(panic::AssertUnwindSafe(move || transform(old_t)))
.unwrap_or_else(|_| panicked_in_transform_mut());
p.write(new_t);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::borrow::Cow;
/// A sample message with custom presence bits, meant to mirror a C++
/// message.
#[derive(Default, Debug)]
struct MyMessage {
/// has a default of `0`
a: i32,
/// has a default of `5`
b: i32,
/// Packed presence bitfield for `a` and `b`
presence: u8,
}
impl MyMessage {
fn a(&self) -> View<'_, VtableProxied> {
VtableProxiedView { val: get_a(self) }
}
fn a_opt(&self) -> Optional<View<'_, VtableProxied>> {
Optional::new(self.a(), has_a(self))
}
fn a_mut(&mut self) -> FieldEntry<'_, VtableProxied> {
static A_VTABLE: ProxyVtable =
ProxyVtable { get: get_a, set: set_a, clear: clear_a, has: has_a };
make_field_entry(self, &A_VTABLE)
}
fn b(&self) -> View<'_, VtableProxied> {
VtableProxiedView { val: get_b(self) }
}
fn b_opt(&self) -> Optional<View<'_, VtableProxied>> {
Optional::new(self.b(), has_b(self))
}
fn b_mut(&mut self) -> FieldEntry<'_, VtableProxied> {
static B_VTABLE: ProxyVtable =
ProxyVtable { get: get_b, set: set_b, clear: clear_b, has: has_b };
make_field_entry(self, &B_VTABLE)
}
}
fn make_field_entry<'a>(
msg: &'a mut MyMessage,
vtable: &'a ProxyVtable,
) -> FieldEntry<'a, VtableProxied> {
if (vtable.has)(&*msg) {
Optional::Set(PresentField::from_inner(Private, VtableProxiedMut { msg, vtable }))
} else {
Optional::Unset(AbsentField::from_inner(Private, VtableProxiedMut { msg, vtable }))
}
}
// Thunks used for the vtable. For a C++ message these would be defined in C++
// and exported via a C API
const A_BIT: u8 = 0;
const B_BIT: u8 = 1;
fn get_a(msg: &MyMessage) -> i32 {
if has_a(msg) { msg.a } else { 0 }
}
fn get_b(msg: &MyMessage) -> i32 {
if has_b(msg) { msg.b } else { 5 }
}
fn set_a(msg: &mut MyMessage, val: i32) {
msg.presence |= (1 << A_BIT);
msg.a = val;
}
fn set_b(msg: &mut MyMessage, val: i32) {
msg.presence |= (1 << B_BIT);
msg.b = val;
}
fn clear_a(msg: &mut MyMessage) {
msg.presence &= !(1 << A_BIT);
}
fn clear_b(msg: &mut MyMessage) {
msg.presence &= !(1 << B_BIT);
}
fn has_a(msg: &MyMessage) -> bool {
msg.presence & (1 << A_BIT) != 0
}
fn has_b(msg: &MyMessage) -> bool {
msg.presence & (1 << B_BIT) != 0
}
struct ProxyVtable {
get: fn(&MyMessage) -> i32,
set: fn(&mut MyMessage, val: i32),
clear: fn(&mut MyMessage),
has: fn(&MyMessage) -> bool,
}
impl Debug for ProxyVtable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Manual `Debug` impl to work around `fmt::Debug` not being implemented for
// functions pointers with higher-ranked lifetimes, which was fixed
// in Rust 1.70.
// TODO(hlopko): replace with `#[derive(Debug)]` when rustc is updated.
f.debug_struct("ProxyVtable")
.field("get", &(self.get as *const ()))
.field("set", &(self.set as *const ()))
.field("clear", &(self.clear as *const ()))
.field("has", &(self.has as *const ()))
.finish()
}
}
/// A proxy for a `i32` that is accessed through methods on a vtable.
struct VtableProxied;
impl Proxied for VtableProxied {
type View<'a> = VtableProxiedView;
type Mut<'a> = VtableProxiedMut<'a>;
}
impl ProxiedWithPresence for VtableProxied {
// In this case, the `PresentMutData` and `AbsentMutData` are identical to the
// `Mut` in layout. Other types/runtimes could require otherwise, e.g. `Mut`
// could be defined to only have get/set functions in its vtable, and not
// has/clear.
type PresentMutData<'a> = VtableProxiedMut<'a>;
type AbsentMutData<'a> = VtableProxiedMut<'a>;
fn clear_present_field<'a>(
present_mutator: Self::PresentMutData<'a>,
) -> Self::AbsentMutData<'a> {
(present_mutator.vtable.clear)(&mut *present_mutator.msg);
present_mutator
}
fn set_absent_to_default<'a>(
absent_mutator: Self::AbsentMutData<'a>,
) -> Self::PresentMutData<'a> {
absent_mutator.as_view().val().set_on_absent(Private, absent_mutator)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct VtableProxiedView {
val: i32,
}
impl VtableProxiedView {
fn val(&self) -> i32 {
self.val
}
fn read(msg: &MyMessage, vtable: &ProxyVtable) -> Self {
VtableProxiedView { val: (vtable.get)(msg) }
}
}
impl<'a> ViewProxy<'a> for VtableProxiedView {
type Proxied = VtableProxied;
fn as_view(&self) -> View<'a, VtableProxied> {
*self
}
fn into_view<'shorter>(self) -> View<'shorter, VtableProxied>
where
'a: 'shorter,
{
self
}
}
#[derive(Debug)]
struct VtableProxiedMut<'a> {
msg: &'a mut MyMessage,
vtable: &'a ProxyVtable,
}
impl<'a> ViewProxy<'a> for VtableProxiedMut<'a> {
type Proxied = VtableProxied;
fn as_view(&self) -> View<'_, VtableProxied> {
VtableProxiedView::read(self.msg, self.vtable)
}
fn into_view<'shorter>(self) -> View<'shorter, VtableProxied>
where
'a: 'shorter,
{
VtableProxiedView::read(self.msg, self.vtable)
}
}
impl<'a> MutProxy<'a> for VtableProxiedMut<'a> {
fn as_mut(&mut self) -> Mut<'_, VtableProxied> {
VtableProxiedMut { msg: self.msg, vtable: self.vtable }
}
fn into_mut<'shorter>(self) -> Mut<'shorter, VtableProxied>
where
'a: 'shorter,
{
self
}
}
impl SettableValue<VtableProxied> for View<'_, VtableProxied> {
fn set_on(self, _private: Private, mutator: Mut<VtableProxied>) {
self.val().set_on(Private, mutator)
}
fn set_on_absent<'a>(
self,
_private: Private,
absent_mutator: <VtableProxied as ProxiedWithPresence>::AbsentMutData<'a>,
) -> <VtableProxied as ProxiedWithPresence>::PresentMutData<'a> {
self.val().set_on_absent(Private, absent_mutator)
}
}
impl SettableValue<VtableProxied> for i32 {
fn set_on(self, _private: Private, mutator: Mut<VtableProxied>) {
(mutator.vtable.set)(mutator.msg, self)
}
fn set_on_absent<'a>(
self,
_private: Private,
absent_mutator: <VtableProxied as ProxiedWithPresence>::AbsentMutData<'a>,
) -> <VtableProxied as ProxiedWithPresence>::PresentMutData<'a> {
(absent_mutator.vtable.set)(absent_mutator.msg, self);
absent_mutator
}
}
#[test]
fn test_field_entry() {
let mut m1 = MyMessage::default();
let mut m2 = MyMessage::default();
let mut m1_a = m1.a_mut();
assert!(matches!(m1_a, Optional::Unset(_)));
assert_eq!(m1_a.as_view().val(), 0);
assert_eq!(m2.b().val(), 5);
let mut m2_b = m2.b_mut();
assert!(m2_b.is_unset());
assert_eq!(m2_b.as_view().val(), 5);
m2_b.set(10);
assert!(m2_b.is_set());
assert!(matches!(m2_b, Optional::Set(_)));
assert_eq!(m2_b.as_view().val(), 10);
assert_eq!(m1_a.or_default().as_view().val(), 0);
assert_eq!(m1.a_opt(), Optional::Set(VtableProxiedView { val: 0 }));
m1.a_mut().clear();
assert_eq!(m1.a().val(), 0);
assert_eq!(m1.b().val(), 5);
assert_eq!(m2.a().val(), 0);
assert_eq!(m2.b().val(), 10);
}
#[test]
fn test_present_field() {
let mut m = MyMessage::default();
m.a_mut().set(10);
match m.a_mut() {
Optional::Set(mut present) => {
assert_eq!(present.as_view().val(), 10);
present.set(20);
assert_eq!(present.as_view().val(), 20);
present.into_mut().set(30);
}
Optional::Unset(_) => unreachable!(),
}
assert_eq!(m.a_opt(), Optional::Set(VtableProxiedView { val: 30 }));
m.b_mut().set(20);
match m.b_mut() {
Optional::Set(present) => present.clear(),
Optional::Unset(_) => unreachable!(),
};
assert_eq!(m.b_opt(), Optional::Unset(VtableProxiedView { val: 5 }));
}
#[test]
fn test_absent_field() {
let mut m = MyMessage::default();
match m.a_mut() {
Optional::Set(_) => unreachable!(),
Optional::Unset(absent) => {
assert_eq!(absent.as_view().val(), 0);
absent.set(20);
}
}
assert_eq!(m.a_opt(), Optional::Set(VtableProxiedView { val: 20 }));
match m.b_mut() {
Optional::Set(_) => unreachable!(),
Optional::Unset(absent) => {
assert_eq!(absent.as_view().val(), 5);
absent.set_default();
}
}
assert_eq!(m.b_opt(), Optional::Set(VtableProxiedView { val: 5 }));
}
}

@ -41,4 +41,4 @@ use protobuf_cpp as kernel;
#[cfg(upb_kernel)]
use protobuf_upb as kernel;
pub use kernel::{Mut, MutProxy, ParseError, Proxied, View, ViewProxy};
pub use kernel::{Mut, MutProxy, ParseError, Proxied, ProxiedWithPresence, View, ViewProxy};

@ -81,7 +81,7 @@ pub trait Proxied {
/// The proxy type that provides shared access to a `T`, like a `&'a T`.
///
/// Most code should use the type alias [`View`].
type View<'a>: ViewProxy<'a, Proxied = Self> + Copy + Send
type View<'a>: ViewProxy<'a, Proxied = Self> + Copy + Send + SettableValue<Self>
where
Self: 'a;
@ -219,14 +219,67 @@ pub trait MutProxy<'a>: ViewProxy<'a> {
'a: 'shorter;
}
/// `Proxied` types that can be optionally set or unset.
///
/// All scalar and message types implement `ProxiedWithPresence`, while repeated
/// types don't.
pub trait ProxiedWithPresence: Proxied {
/// The data necessary to store a present field mutator proxying `Self`.
/// This is the contents of `PresentField<'a, Self>`.
type PresentMutData<'a>: MutProxy<'a, Proxied = Self>;
/// The data necessary to store an absent field mutator proxying `Self`.
/// This is the contents of `AbsentField<'a, Self>`.
type AbsentMutData<'a>: ViewProxy<'a, Proxied = Self>;
/// Clears a present field.
fn clear_present_field<'a>(
present_mutator: Self::PresentMutData<'a>,
) -> Self::AbsentMutData<'a>;
/// Sets an absent field to its default value.
///
/// This can be more efficient than setting with a default value, e.g.
/// a default submessage could share resources with the parent message.
fn set_absent_to_default<'a>(
absent_mutator: Self::AbsentMutData<'a>,
) -> Self::PresentMutData<'a>;
}
/// Values that can be used to set a field of `T`.
pub trait SettableValue<T>
pub trait SettableValue<T>: Sized
where
T: Proxied + ?Sized,
{
/// Consumes `self` to set the given mutator to its value.
#[doc(hidden)]
fn set_on(self, _private: Private, mutator: Mut<T>);
fn set_on(self, _private: Private, mutator: Mut<'_, T>);
/// Consumes `self` and `absent_mutator` to set the given empty field to
/// a value.
#[doc(hidden)]
fn set_on_absent<'a>(
self,
_private: Private,
absent_mutator: T::AbsentMutData<'a>,
) -> T::PresentMutData<'a>
where
T: ProxiedWithPresence,
{
let mut present = T::set_absent_to_default(absent_mutator);
self.set_on(Private, present.as_mut());
present
}
/// Consumes `self` and `present_mutator` to set the given present field
/// to a value.
#[doc(hidden)]
fn set_on_present(self, _private: Private, mut present_mutator: T::PresentMutData<'_>)
where
T: ProxiedWithPresence,
{
self.set_on(Private, present_mutator.as_mut())
}
}
#[cfg(test)]
@ -312,6 +365,12 @@ mod tests {
}
}
impl SettableValue<MyProxied> for MyProxiedView<'_> {
fn set_on(self, _private: Private, mutator: Mut<MyProxied>) {
mutator.my_proxied_ref.val = self.my_proxied_ref.val.clone();
}
}
impl SettableValue<MyProxied> for String {
fn set_on(self, _private: Private, mutator: Mut<MyProxied>) {
mutator.my_proxied_ref.val = self;

@ -47,7 +47,7 @@ mod optional;
mod proxied;
pub use optional::{AbsentField, FieldEntry, Optional, PresentField};
pub use proxied::{Mut, MutProxy, Proxied, SettableValue, View, ViewProxy};
pub use proxied::{Mut, MutProxy, Proxied, ProxiedWithPresence, SettableValue, View, ViewProxy};
/// Everything in `__internal` is allowed to change without it being considered
/// a breaking change for the protobuf library. Nothing in here should be

Loading…
Cancel
Save