diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:41 +0000 |
commit | 4f9fe856a25ab29345b90e7725509e9ee38a37be (patch) | |
tree | e4ffd8a9374cae7b21f7cbfb352927e0e074aff6 /vendor/yoke/src | |
parent | Adding upstream version 1.68.2+dfsg1. (diff) | |
download | rustc-upstream/1.69.0+dfsg1.tar.xz rustc-upstream/1.69.0+dfsg1.zip |
Adding upstream version 1.69.0+dfsg1.upstream/1.69.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/yoke/src')
-rw-r--r-- | vendor/yoke/src/erased.rs | 8 | ||||
-rw-r--r-- | vendor/yoke/src/is_covariant.rs | 142 | ||||
-rw-r--r-- | vendor/yoke/src/lib.rs | 2 | ||||
-rw-r--r-- | vendor/yoke/src/macro_impls.rs | 3 | ||||
-rw-r--r-- | vendor/yoke/src/yoke.rs | 228 | ||||
-rw-r--r-- | vendor/yoke/src/zero_from.rs | 5 |
6 files changed, 209 insertions, 179 deletions
diff --git a/vendor/yoke/src/erased.rs b/vendor/yoke/src/erased.rs index c314d5186..bc4de9791 100644 --- a/vendor/yoke/src/erased.rs +++ b/vendor/yoke/src/erased.rs @@ -7,7 +7,7 @@ //! See the docs of [`Yoke::erase_rc_cart()`](crate::Yoke::erase_rc_cart) //! and [`Yoke::erase_box_cart()`](crate::Yoke::erase_box_cart) for more info. //! -//! Available with the `"alloc"` feature enabled. +//! Available with the `"alloc"` Cargo feature enabled. use alloc::boxed::Box; use alloc::rc::Rc; @@ -25,17 +25,17 @@ impl<T: 'static> ErasedDestructor for T {} /// /// See the docs of [`Yoke::erase_arc_cart()`](crate::Yoke::erase_rc_cart) for more info. /// -/// Available with the `"alloc"` feature enabled. +/// Available with the `"alloc"` Cargo feature enabled. pub type ErasedArcCart = Arc<dyn ErasedDestructor + Send + Sync>; /// A type-erased Cart that has `Rc` semantics /// /// See the docs of [`Yoke::erase_rc_cart()`](crate::Yoke::erase_rc_cart) for more info. /// -/// Available with the `"alloc"` feature enabled. +/// Available with the `"alloc"` Cargo feature enabled. pub type ErasedRcCart = Rc<dyn ErasedDestructor>; /// A type-erased Cart that has `Box` semantics /// /// See the docs of [`Yoke::erase_box_cart()`](crate::Yoke::erase_box_cart) for more info. /// -/// Available with the `"alloc"` feature enabled. +/// Available with the `"alloc"` Cargo feature enabled. pub type ErasedBoxCart = Box<dyn ErasedDestructor>; diff --git a/vendor/yoke/src/is_covariant.rs b/vendor/yoke/src/is_covariant.rs deleted file mode 100644 index 75d123c84..000000000 --- a/vendor/yoke/src/is_covariant.rs +++ /dev/null @@ -1,142 +0,0 @@ -// This file is part of ICU4X. For terms of use, please see the file -// called LICENSE at the top level of the ICU4X source tree -// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). - -#[cfg(feature = "alloc")] -use alloc::{ - borrow::{Cow, ToOwned}, - boxed::Box, - rc::Rc, - string::String, -}; - -/// A type implementing `IsCovariant<'a>` is covariant with respect to lifetime `'a`. -/// -/// Lifetime parameters that are safely cast in [`Yokeable`] are also valid for `IsCovariant`. -/// -/// `IsCovariant` exists primarily to serve in trait bounds. The primary use case is to safely -/// perform lifetime casting on trait objects (`dyn Trait`). This enables a type-erased [`Yoke`] -/// consisting of only trait objects. See the examples. -/// -/// `IsCovariant` is auto-implemented in [`#[derive(Yokeable)]`](macro@crate::Yokeable). -/// -/// # Safety -/// -/// This trait is safe to implement on types with a _covariant_ lifetime parameter. This will -/// occur when the lifetime parameter is used within references, but not in the arguments of -/// function pointers or in mutable positions (either in `&mut` or via interior mutability). -/// -/// If a struct has multiple lifetime parameters, only the one used in `IsCovariant<'a>` needs to -/// be covariant. -/// -/// # Examples -/// -/// Implementing on a simple struct with a single covariant lifetime: -/// -/// ``` -/// # use yoke::*; -/// struct MyStruct<'a>(&'a str); -/// -/// // This is safe because 'a is covariant -/// unsafe impl<'a> IsCovariant<'a> for MyStruct<'a> {} -/// ``` -/// -/// By constraining the trait `ExampleTrait<'a>` on `IsCovariant<'a>`, we can safely implement -/// [`Yokeable`] and [`ZeroFrom`] on its trait object: -/// -/// ``` -/// # use yoke::*; -/// # use zerofrom::*; -/// # use core::mem; -/// trait ExampleTrait<'a>: IsCovariant<'a> { -/// fn get_message(&self) -> &'a str; -/// } -/// -/// // This wrapper is required because of the blanket Yokeable impl on &'static T -/// pub struct ExampleTraitDynRef<'a>(pub &'a dyn ExampleTrait<'a>); -/// -/// // The following impl is safe because the trait object requires IsCovariant. -/// unsafe impl<'a> Yokeable<'a> for ExampleTraitDynRef<'static> { -/// type Output = ExampleTraitDynRef<'a>; -/// fn transform(&'a self) -> &'a Self::Output { -/// unsafe { mem::transmute(self) } -/// } -/// -/// fn transform_owned(self) -> Self::Output { -/// unsafe { mem::transmute(self) } -/// } -/// -/// unsafe fn make(from: Self::Output) -> Self { -/// unsafe { mem::transmute(from) } -/// } -/// -/// fn transform_mut<F>(&'a mut self, f: F) -/// where -/// F: 'static + FnOnce(&'a mut Self::Output), -/// { -/// unsafe { f(mem::transmute::<&mut Self, &mut Self::Output>(self)) } -/// } -/// } -/// -/// impl<'zf, 'a> ZeroFrom<'zf, dyn ExampleTrait<'a> + 'a> for ExampleTraitDynRef<'zf> { -/// fn zero_from(this: &'zf (dyn ExampleTrait<'a> + 'a)) -> ExampleTraitDynRef<'zf> { -/// // This is safe because the trait object requires IsCovariant. -/// ExampleTraitDynRef(unsafe { core::mem::transmute(this) }) -/// } -/// } -/// -/// // Implement ExampleTrait on the struct from the previous example -/// # struct MyStruct<'a>(&'a str); -/// # unsafe impl<'a> IsCovariant<'a> for MyStruct<'a> {} -/// impl<'a> ExampleTrait<'a> for MyStruct<'a> { -/// fn get_message(&self) -> &'a str { -/// self.0 -/// } -/// } -/// -/// // Example usage: a Yoke of a trait object -/// let s = "Hello World".to_string(); -/// let yoke: Yoke<ExampleTraitDynRef<'static>, Box<dyn ExampleTrait>> = -/// Yoke::attach_to_zero_copy_cart(Box::new(MyStruct(&s))); -/// -/// assert_eq!(yoke.get().0.get_message(), "Hello World"); -/// ``` -/// -/// [`Yoke`]: crate::Yoke -/// [`Yokeable`]: crate::Yokeable -/// [`ZeroFrom`]: crate::ZeroFrom -pub unsafe trait IsCovariant<'a>: 'a {} - -// IsCovariant is implemented on the standard library Copy types in macro_impls.rs - -// The following impls are safe because there is only one lifetime, 'a, and 'a is covariant - -unsafe impl<'a> IsCovariant<'a> for () {} - -unsafe impl<'a> IsCovariant<'a> for str {} -#[cfg(feature = "alloc")] -unsafe impl<'a> IsCovariant<'a> for String {} - -unsafe impl<'a, T: IsCovariant<'a>> IsCovariant<'a> for Option<T> {} - -unsafe impl<'a, T1: IsCovariant<'a>, T2: IsCovariant<'a>> IsCovariant<'a> for (T1, T2) {} - -unsafe impl<'a, T: IsCovariant<'a>> IsCovariant<'a> for [T] {} - -unsafe impl<'a, T: IsCovariant<'a>, const N: usize> IsCovariant<'a> for [T; N] {} - -#[cfg(feature = "alloc")] -unsafe impl<'a, T: IsCovariant<'a> + ?Sized> IsCovariant<'a> for Box<T> {} - -#[cfg(feature = "alloc")] -unsafe impl<'a, T: IsCovariant<'a> + ?Sized> IsCovariant<'a> for Rc<T> {} - -// This is safe because T has a covariant lifetime, and Cow's lifetime is also covariant -#[cfg(feature = "alloc")] -unsafe impl<'a, T: IsCovariant<'a> + ToOwned + ?Sized> IsCovariant<'a> for Cow<'a, T> where - <T as ToOwned>::Owned: Sized -{ -} - -// This is safe because T has a covariant lifetime, and the reference lifetime is also covariant -unsafe impl<'a, T: IsCovariant<'a> + ?Sized> IsCovariant<'a> for &'a T {} diff --git a/vendor/yoke/src/lib.rs b/vendor/yoke/src/lib.rs index 666e179e9..1524c067c 100644 --- a/vendor/yoke/src/lib.rs +++ b/vendor/yoke/src/lib.rs @@ -48,7 +48,6 @@ extern crate alloc; pub mod either; #[cfg(feature = "alloc")] pub mod erased; -mod is_covariant; mod macro_impls; pub mod trait_hack; mod yoke; @@ -59,7 +58,6 @@ mod zero_from; #[cfg(feature = "derive")] pub use yoke_derive::Yokeable; -pub use crate::is_covariant::IsCovariant; pub use crate::yoke::{CloneableCart, Yoke}; pub use crate::yokeable::Yokeable; diff --git a/vendor/yoke/src/macro_impls.rs b/vendor/yoke/src/macro_impls.rs index 664816818..060061b20 100644 --- a/vendor/yoke/src/macro_impls.rs +++ b/vendor/yoke/src/macro_impls.rs @@ -6,7 +6,7 @@ // than using pointer casts #![allow(clippy::transmute_ptr_to_ptr)] -use crate::{IsCovariant, Yokeable}; +use crate::Yokeable; use core::{mem, ptr}; macro_rules! copy_yoke_impl { @@ -38,7 +38,6 @@ macro_rules! impl_copy_type { type Output = Self; copy_yoke_impl!(); } - unsafe impl<'a> IsCovariant<'a> for $ty {} }; } diff --git a/vendor/yoke/src/yoke.rs b/vendor/yoke/src/yoke.rs index c3d8c37d9..7468b4d99 100644 --- a/vendor/yoke/src/yoke.rs +++ b/vendor/yoke/src/yoke.rs @@ -6,7 +6,6 @@ use crate::either::EitherCart; #[cfg(feature = "alloc")] use crate::erased::{ErasedArcCart, ErasedBoxCart, ErasedRcCart}; use crate::trait_hack::YokeTraitHack; -use crate::IsCovariant; use crate::Yokeable; use core::marker::PhantomData; use core::ops::Deref; @@ -48,8 +47,7 @@ use alloc::sync::Arc; /// into another `Yoke` containing a different type that may contain elements of the original yoked /// value. See the [`Yoke::map_project()`] docs for more details. /// -/// In general, `C` is a concrete type, but it is also possible for it to be a trait object; -/// for more information, see [`IsCovariant`]. +/// In general, `C` is a concrete type, but it is also possible for it to be a trait object. /// /// # Example /// @@ -83,7 +81,10 @@ pub struct Yoke<Y: for<'a> Yokeable<'a>, C> { cart: C, } -impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C> { +impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C> +where + <C as Deref>::Target: 'static, +{ /// Construct a [`Yoke`] by yokeing an object to a cart in a closure. /// /// See also [`Yoke::try_attach_to_cart()`] to return a `Result` from the closure. @@ -116,7 +117,14 @@ impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C> { /// ``` pub fn attach_to_cart<F>(cart: C, f: F) -> Self where + // safety note: This works by enforcing that the *only* place the return value of F + // can borrow from is the cart, since `F` must be valid for all lifetimes `'de` + // + // The <C as Deref>::Target: 'static on the impl is crucial for safety as well + // + // See safety docs at the bottom of this file for more information F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output, + <C as Deref>::Target: 'static, { let deserialized = f(cart.deref()); Self { @@ -220,8 +228,10 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// ``` /// use yoke::Yoke; /// - /// let local_data = "foo".to_string(); - /// let yoke = Yoke::<&'static str, Box<String>>::attach_to_zero_copy_cart(Box::new(local_data)); + /// let local_data = "foo".to_owned(); + /// let yoke = Yoke::<&'static str, Box<String>>::attach_to_zero_copy_cart( + /// Box::new(local_data), + /// ); /// assert_eq!(*yoke.get(), "foo"); /// /// // Get back the cart @@ -235,9 +245,11 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// use std::borrow::Cow; /// use yoke::Yoke; /// - /// let local_data = "foo".to_string(); + /// let local_data = "foo".to_owned(); /// let mut yoke = - /// Yoke::<Cow<'static, str>, Box<String>>::attach_to_zero_copy_cart(Box::new(local_data)); + /// Yoke::<Cow<'static, str>, Box<String>>::attach_to_zero_copy_cart( + /// Box::new(local_data), + /// ); /// assert_eq!(yoke.get(), "foo"); /// /// // Override data in the cart @@ -265,6 +277,9 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// - `f()` must not panic /// - References from the yokeable `Y` should still be valid for the lifetime of the /// returned cart type `C`. + /// - Lifetimes inside C must not be lengthened, even if they are themselves contravariant. + /// I.e., if C contains an `fn(&'a u8)`, it cannot be replaced with `fn(&'static u8), + /// even though that is typically safe. /// /// Typically, this means implementing `f` as something which _wraps_ the inner cart type `C`. /// `Yoke` only really cares about destructors for its carts so it's fine to erase other @@ -506,12 +521,9 @@ where } } -// This is safe because Y is 'static and C has a covariant lifetime -unsafe impl<'b, Y: for<'a> Yokeable<'a>, C: IsCovariant<'b>> IsCovariant<'b> for Yoke<Y, C> {} - #[test] fn test_clone() { - let local_data = "foo".to_string(); + let local_data = "foo".to_owned(); let y1 = Yoke::<alloc::borrow::Cow<'static, str>, Rc<String>>::attach_to_zero_copy_cart( Rc::new(local_data), ); @@ -575,7 +587,9 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// string_2: &'a str, /// } /// - /// fn map_project_string_1(bar: Yoke<Bar<'static>, Rc<[u8]>>) -> Yoke<&'static str, Rc<[u8]>> { + /// fn map_project_string_1( + /// bar: Yoke<Bar<'static>, Rc<[u8]>>, + /// ) -> Yoke<&'static str, Rc<[u8]>> { /// bar.map_project(|bar, _| bar.string_1) /// } /// @@ -651,7 +665,9 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// # use yoke::Yoke; /// # use std::str::{self, Utf8Error}; /// # - /// fn slice(y: Yoke<&'static [u8], Rc<[u8]>>) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> { + /// fn slice( + /// y: Yoke<&'static [u8], Rc<[u8]>>, + /// ) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> { /// y.try_map_project(move |bytes, _| str::from_utf8(bytes)) /// } /// ``` @@ -671,7 +687,9 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// string_2: &'a str, /// } /// - /// fn map_project_string_1(bar: Yoke<Bar<'static>, Rc<[u8]>>) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> { + /// fn map_project_string_1( + /// bar: Yoke<Bar<'static>, Rc<[u8]>>, + /// ) -> Result<Yoke<&'static str, Rc<[u8]>>, Utf8Error> { /// bar.try_map_project(|bar, _| str::from_utf8(bar.bytes_1)) /// } /// @@ -861,17 +879,19 @@ impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized> Yoke<Y, Rc<C>> { /// let buffer1: Rc<String> = Rc::new(" foo bar baz ".into()); /// let buffer2: Box<String> = Box::new(" baz quux ".into()); /// - /// let yoke1 = Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim()); + /// let yoke1 = + /// Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim()); /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim()); /// /// let erased1: Yoke<_, ErasedRcCart> = yoke1.erase_rc_cart(); /// // Wrap the Box in an Rc to make it compatible - /// let erased2: Yoke<_, ErasedRcCart> = yoke2.wrap_cart_in_rc().erase_rc_cart(); + /// let erased2: Yoke<_, ErasedRcCart> = + /// yoke2.wrap_cart_in_rc().erase_rc_cart(); /// /// // Now erased1 and erased2 have the same type! /// ``` /// - /// Available with the `"alloc"` feature enabled. + /// Available with the `"alloc"` Cargo feature enabled. pub fn erase_rc_cart(self) -> Yoke<Y, ErasedRcCart> { unsafe { // safe because the cart is preserved, just @@ -905,17 +925,19 @@ impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized + Send + Sync> Yoke<Y, Arc<C>> /// let buffer1: Arc<String> = Arc::new(" foo bar baz ".into()); /// let buffer2: Box<String> = Box::new(" baz quux ".into()); /// - /// let yoke1 = Yoke::<&'static str, _>::attach_to_cart(buffer1, |arc| arc.trim()); + /// let yoke1 = + /// Yoke::<&'static str, _>::attach_to_cart(buffer1, |arc| arc.trim()); /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim()); /// /// let erased1: Yoke<_, ErasedArcCart> = yoke1.erase_arc_cart(); /// // Wrap the Box in an Rc to make it compatible - /// let erased2: Yoke<_, ErasedArcCart> = yoke2.wrap_cart_in_arc().erase_arc_cart(); + /// let erased2: Yoke<_, ErasedArcCart> = + /// yoke2.wrap_cart_in_arc().erase_arc_cart(); /// /// // Now erased1 and erased2 have the same type! /// ``` /// - /// Available with the `"alloc"` feature enabled. + /// Available with the `"alloc"` Cargo feature enabled. pub fn erase_arc_cart(self) -> Yoke<Y, ErasedArcCart> { unsafe { // safe because the cart is preserved, just @@ -949,17 +971,19 @@ impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized> Yoke<Y, Box<C>> { /// let buffer1: Rc<String> = Rc::new(" foo bar baz ".into()); /// let buffer2: Box<String> = Box::new(" baz quux ".into()); /// - /// let yoke1 = Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim()); + /// let yoke1 = + /// Yoke::<&'static str, _>::attach_to_cart(buffer1, |rc| rc.trim()); /// let yoke2 = Yoke::<&'static str, _>::attach_to_cart(buffer2, |b| b.trim()); /// /// // Wrap the Rc in an Box to make it compatible - /// let erased1: Yoke<_, ErasedBoxCart> = yoke1.wrap_cart_in_box().erase_box_cart(); + /// let erased1: Yoke<_, ErasedBoxCart> = + /// yoke1.wrap_cart_in_box().erase_box_cart(); /// let erased2: Yoke<_, ErasedBoxCart> = yoke2.erase_box_cart(); /// /// // Now erased1 and erased2 have the same type! /// ``` /// - /// Available with the `"alloc"` feature enabled. + /// Available with the `"alloc"` Cargo feature enabled. pub fn erase_box_cart(self) -> Yoke<Y, ErasedBoxCart> { unsafe { // safe because the cart is preserved, just @@ -974,7 +998,7 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// Helper function allowing one to wrap the cart type `C` in a `Box<T>`. /// Can be paired with [`Yoke::erase_box_cart()`] /// - /// Available with the `"alloc"` feature enabled. + /// Available with the `"alloc"` Cargo feature enabled. #[inline] pub fn wrap_cart_in_box(self) -> Yoke<Y, Box<C>> { unsafe { @@ -986,7 +1010,7 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// Can be paired with [`Yoke::erase_rc_cart()`], or generally used /// to make the [`Yoke`] cloneable. /// - /// Available with the `"alloc"` feature enabled. + /// Available with the `"alloc"` Cargo feature enabled. #[inline] pub fn wrap_cart_in_rc(self) -> Yoke<Y, Rc<C>> { unsafe { @@ -998,7 +1022,7 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// Can be paired with [`Yoke::erase_arc_cart()`], or generally used /// to make the [`Yoke`] cloneable. /// - /// Available with the `"alloc"` feature enabled. + /// Available with the `"alloc"` Cargo feature enabled. #[inline] pub fn wrap_cart_in_arc(self) -> Yoke<Y, Arc<C>> { unsafe { @@ -1037,7 +1061,7 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { } } -/// Safety docs for project() +/// # Safety docs for project() /// /// (Docs are on a private const to allow the use of compile_fail doctests) /// @@ -1140,3 +1164,151 @@ impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> { /// the output yokeable can _only_ have borrowed data flow in to it from the input. All paths of unsoundness require the /// unification of an existential and universal lifetime, which isn't possible. const _: () = (); + +/// # Safety docs for attach_to_cart()'s signature +/// +/// The `attach_to_cart()` family of methods get by by using the following bound: +/// +/// ```rust,ignore +/// F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output, +/// C::Target: 'static +/// ``` +/// +/// to enforce that the yoking closure produces a yokeable that is *only* allowed to borrow from the cart. +/// A way to be sure of this is as follows: imagine if `F` *did* borrow data of lifetime `'a` and stuff it in +/// its output. Then that lifetime `'a` would have to live at least as long as `'de` *for all `'de`*. +/// The only lifetime that satisfies that is `'static` (since at least one of the potential `'de`s is `'static`), +/// and we're fine with that. +/// +/// ## Implied bounds and variance +/// +/// The `C::Target: 'static` bound is tricky, however. Let's imagine a situation where we *didn't* have that bound. +/// +/// One thing to remember is that we are okay with the cart itself borrowing from places, +/// e.g. `&[u8]` is a valid cart, as is `Box<&[u8]>`. `C` is not `'static`. +/// +/// (I'm going to use `CT` in prose to refer to `C::Target` here, since almost everything here has to do +/// with C::Target and not C itself.) +/// +/// Unfortunately, there's a sneaky additional bound inside `F`. The signature of `F` is *actually* +/// +/// ```rust,ignore +/// F: for<'de> where<C::Target: 'de> FnOnce(&'de C::Target) -> <Y as Yokeable<'de>>::Output +/// ``` +/// +/// using made-up "where clause inside HRTB" syntax to represent a type that can be represented inside the compiler +/// and type system but not in Rust code. The `CT: 'de` bond comes from the `&'de C::Target`: any time you +/// write `&'a T`, an implied bound of `T: 'a` materializes and is stored alongside it, since references cannot refer +/// to data that itself refers to data of shorter lifetimes. If a reference is valid, its referent must be valid for +/// the duration of the reference's lifetime, so every reference *inside* its referent must also be valid, giving us `T: 'a`. +/// This kind of constraint is often called a "well formedness" constraint: `&'a T` is not "well formed" without that +/// bound, and rustc is being helpful by giving it to us for free. +/// +/// Unfortunately, this messes with our universal quantification. The `for<'de>` is no longer "For all lifetimes `'de`", +/// it is "for all lifetimes `'de` *where `CT: 'de`*". And if `CT` borrows from somewhere (with lifetime `'ct`), then we get a +/// `'ct: 'de` bound, and `'de` candidates that live longer than `'ct` won't actually be considered. +/// The neat little logic at the beginning stops working. +/// +/// `attach_to_cart()` will instead enforce that the produced yokeable *either* borrows from the cart (fine), or from +/// data that has a lifetime that is at least `'ct`. Which means that `attach_to_cart()` will allow us to borrow locals +/// provided they live at least as long as `'ct`. +/// +/// Is this a problem? +/// +/// This is totally fine if CT's lifetime is covariant: if C is something like `Box<&'ct [u8]>`, even if our +/// yoked object borrows from locals outliving `'ct`, our Yoke can't outlive that +/// lifetime `'ct` anyway (since it's a part of the cart type), so we're fine. +/// +/// However it's completely broken for contravariant carts (e.g. `Box<fn(&'ct u8)>`). In that case +/// we still get `'ct: 'de`, and we still end up being able to +/// borrow from locals that outlive `'ct`. However, our Yoke _can_ outlive +/// that lifetime, because Yoke shares its variance over `'ct` +/// with the cart type, and the cart type is contravariant over `'ct`. +/// So the Yoke can be upcast to having a longer lifetime than `'ct`, and *that* Yoke +/// can outlive `'ct`. +/// +/// We fix this by forcing `C::Target: 'static` in `attach_to_cart()`, which would make it work +/// for fewer types, but would also allow Yoke to continue to be covariant over cart lifetimes if necessary. +/// +/// An alternate fix would be to not allowing yoke to ever be upcast over lifetimes contained in the cart +/// by forcing them to be invariant. This is a bit more restrictive and affects *all* `Yoke` users, not just +/// those using `attach_to_cart()`. +/// +/// See https://github.com/unicode-org/icu4x/issues/2926 +/// See also https://github.com/rust-lang/rust/issues/106431 for potentially fixing this upstream by +/// changing how the bound works. +/// +/// # Tests +/// +/// Here's a broken `attach_to_cart()` that attempts to borrow from a local: +/// +/// ```rust,compile_fail +/// use yoke::{Yoke, Yokeable}; +/// +/// let cart = vec![1, 2, 3, 4].into_boxed_slice(); +/// let local = vec![4, 5, 6, 7]; +/// let yoke: Yoke<&[u8], Box<[u8]>> = Yoke::attach_to_cart(cart, |_| &*local); +/// ``` +/// +/// Fails as expected. +/// +/// And here's a working one with a local borrowed cart that does not do any sneaky borrows whilst attaching. +/// +/// ```rust +/// use yoke::{Yoke, Yokeable}; +/// +/// let cart = vec![1, 2, 3, 4].into_boxed_slice(); +/// let local = vec![4, 5, 6, 7]; +/// let yoke: Yoke<&[u8], &[u8]> = Yoke::attach_to_cart(&cart, |c| &*c); +/// ``` +/// +/// Here's an `attach_to_cart()` that attempts to borrow from a longer-lived local due to +/// the cart being covariant. It fails, but would not if the alternate fix of forcing Yoke to be invariant +/// were implemented. It is technically a safe operation: +/// +/// ```rust,compile_fail +/// use yoke::{Yoke, Yokeable}; +/// // longer lived +/// let local = vec![4, 5, 6, 7]; +/// +/// let backing = vec![1, 2, 3, 4]; +/// let cart = Box::new(&*backing); +/// +/// let yoke: Yoke<&[u8], Box<&[u8]>> = Yoke::attach_to_cart(cart, |_| &*local); +/// println!("{:?}", yoke.get()); +/// ``` +/// +/// Finally, here's an `attach_to_cart()` that attempts to borrow from a longer lived local +/// in the case of a contravariant lifetime. It does not compile, but in and of itself is not dangerous: +/// +/// ```rust,compile_fail +/// use yoke::Yoke; +/// +/// type Contra<'a> = fn(&'a ()); +/// +/// let local = String::from("Hello World!"); +/// let yoke: Yoke<&'static str, Box<Contra<'_>>> = Yoke::attach_to_cart(Box::new((|_| {}) as _), |_| &local[..]); +/// println!("{:?}", yoke.get()); +/// ``` +/// +/// It is dangerous if allowed to transform (testcase from #2926) +/// +/// ```rust,compile_fail +/// use yoke::Yoke; +/// +/// type Contra<'a> = fn(&'a ()); +/// +/// +/// let local = String::from("Hello World!"); +/// let yoke: Yoke<&'static str, Box<Contra<'_>>> = Yoke::attach_to_cart(Box::new((|_| {}) as _), |_| &local[..]); +/// println!("{:?}", yoke.get()); +/// let yoke_longer: Yoke<&'static str, Box<Contra<'static>>> = yoke; +/// let leaked: &'static Yoke<&'static str, Box<Contra<'static>>> = Box::leak(Box::new(yoke_longer)); +/// let reference: &'static str = leaked.get(); +/// +/// println!("pre-drop: {reference}"); +/// drop(local); +/// println!("post-drop: {reference}"); +/// +/// ``` +const _: () = (); diff --git a/vendor/yoke/src/zero_from.rs b/vendor/yoke/src/zero_from.rs index 679a28d59..d876d9c06 100644 --- a/vendor/yoke/src/zero_from.rs +++ b/vendor/yoke/src/zero_from.rs @@ -26,6 +26,7 @@ where Y: for<'a> Yokeable<'a>, for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: ZeroFrom<'a, <C as Deref>::Target>, C: StableDeref + Deref, + <C as Deref>::Target: 'static, { /// Construct a [`Yoke`]`<Y, C>` from a cart implementing `StableDeref` by zero-copy cloning /// the cart to `Y` and then yokeing that object to the cart. @@ -42,7 +43,9 @@ where /// use std::borrow::Cow; /// use yoke::Yoke; /// - /// let yoke = Yoke::<Cow<'static, str>, String>::attach_to_zero_copy_cart("demo".to_string()); + /// let yoke = Yoke::<Cow<'static, str>, String>::attach_to_zero_copy_cart( + /// "demo".to_owned(), + /// ); /// /// assert_eq!("demo", yoke.get()); /// ``` |