From a646b3996510fb79a8fd4701f888542e9b280504 Mon Sep 17 00:00:00 2001 From: wayne warren Date: Thu, 6 Jul 2023 10:53:52 -0600 Subject: [PATCH] core/any: remove Provider trait * remove `impl Provider for Error` * rename `Demand` to `Request` * update docstrings to focus on the conceptual API provided by `Request` * move `core::any::{request_ref, request_value}` functions into `core::error` * move `core::any::tag`, `core::any::Request`, an `core::any::TaggedOption` into `core::error` * replace `provide_any` feature name w/ `error_generic_member_access` * move `core::error::request_{ref,value} tests into core::tests::error module * update unit and doc tests --- library/alloc/src/lib.rs | 1 - library/alloc/src/sync.rs | 2 +- library/core/src/any.rs | 587 -------------------------- library/core/src/error.rs | 735 +++++++++++++++++++++++++++++---- library/core/tests/any.rs | 62 --- library/core/tests/error.rs | 66 +++ library/core/tests/lib.rs | 4 +- library/std/src/error.rs | 13 +- library/std/src/error/tests.rs | 4 +- library/std/src/lib.rs | 1 - 10 files changed, 736 insertions(+), 739 deletions(-) create mode 100644 library/core/tests/error.rs diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 80681a7a7cfed..cb8691aac138f 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -138,7 +138,6 @@ #![feature(maybe_uninit_uninit_array_transpose)] #![feature(pattern)] #![feature(pointer_byte_offsets)] -#![feature(provide_any)] #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 6c701225a848f..e2a2fe932ab27 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -3575,7 +3575,7 @@ impl core::error::Error for Arc { core::error::Error::source(&**self) } - fn provide<'a>(&'a self, req: &mut core::any::Demand<'a>) { + fn provide<'a>(&'a self, req: &mut core::error::Request<'a>) { core::error::Error::provide(&**self, req); } } diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 277c2f76a0864..8f5404d9713dd 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -83,72 +83,6 @@ //! } //! ``` //! -//! # `Provider` and `Demand` -//! -//! `Provider` and the associated APIs support generic, type-driven access to data, and a mechanism -//! for implementers to provide such data. The key parts of the interface are the `Provider` -//! trait for objects which can provide data, and the [`request_value`] and [`request_ref`] -//! functions for requesting data from an object which implements `Provider`. Generally, end users -//! should not call `request_*` directly, they are helper functions for intermediate implementers -//! to use to implement a user-facing interface. This is purely for the sake of ergonomics, there is -//! no safety concern here; intermediate implementers can typically support methods rather than -//! free functions and use more specific names. -//! -//! Typically, a data provider is a trait object of a trait which extends `Provider`. A user will -//! request data from a trait object by specifying the type of the data. -//! -//! ## Data flow -//! -//! * A user requests an object of a specific type, which is delegated to `request_value` or -//! `request_ref` -//! * `request_*` creates a `Demand` object and passes it to `Provider::provide` -//! * The data provider's implementation of `Provider::provide` tries providing values of -//! different types using `Demand::provide_*`. If the type matches the type requested by -//! the user, the value will be stored in the `Demand` object. -//! * `request_*` unpacks the `Demand` object and returns any stored value to the user. -//! -//! ## Examples -//! -//! ``` -//! # #![feature(provide_any)] -//! use std::any::{Provider, Demand, request_ref}; -//! -//! // Definition of MyTrait, a data provider. -//! trait MyTrait: Provider { -//! // ... -//! } -//! -//! // Methods on `MyTrait` trait objects. -//! impl dyn MyTrait + '_ { -//! /// Get a reference to a field of the implementing struct. -//! pub fn get_context_by_ref(&self) -> Option<&T> { -//! request_ref::(self) -//! } -//! } -//! -//! // Downstream implementation of `MyTrait` and `Provider`. -//! # struct SomeConcreteType { some_string: String } -//! impl MyTrait for SomeConcreteType { -//! // ... -//! } -//! -//! impl Provider for SomeConcreteType { -//! fn provide<'a>(&'a self, demand: &mut Demand<'a>) { -//! // Provide a string reference. We could provide multiple values with -//! // different types here. -//! demand.provide_ref::(&self.some_string); -//! } -//! } -//! -//! // Downstream usage of `MyTrait`. -//! fn use_my_trait(obj: &dyn MyTrait) { -//! // Request a &String from obj. -//! let _ = obj.get_context_by_ref::().unwrap(); -//! } -//! ``` -//! -//! In this example, if the concrete type of `obj` in `use_my_trait` is `SomeConcreteType`, then -//! the `get_context_by_ref` call will return a reference to `obj.some_string` with type `&String`. #![stable(feature = "rust1", since = "1.0.0")] @@ -798,524 +732,3 @@ pub const fn type_name() -> &'static str { pub const fn type_name_of_val(_val: &T) -> &'static str { type_name::() } - -/////////////////////////////////////////////////////////////////////////////// -// Provider trait -/////////////////////////////////////////////////////////////////////////////// - -/// Trait implemented by a type which can dynamically provide values based on type. -#[unstable(feature = "provide_any", issue = "96024")] -pub trait Provider { - /// Data providers should implement this method to provide *all* values they are able to - /// provide by using `demand`. - /// - /// Note that the `provide_*` methods on `Demand` have short-circuit semantics: if an earlier - /// method has successfully provided a value, then later methods will not get an opportunity to - /// provide. - /// - /// # Examples - /// - /// Provides a reference to a field with type `String` as a `&str`, and a value of - /// type `i32`. - /// - /// ```rust - /// # #![feature(provide_any)] - /// use std::any::{Provider, Demand}; - /// # struct SomeConcreteType { field: String, num_field: i32 } - /// - /// impl Provider for SomeConcreteType { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_ref::(&self.field) - /// .provide_value::(self.num_field); - /// } - /// } - /// ``` - #[unstable(feature = "provide_any", issue = "96024")] - fn provide<'a>(&'a self, demand: &mut Demand<'a>); -} - -/// Request a value from the `Provider`. -/// -/// # Examples -/// -/// Get a string value from a provider. -/// -/// ```rust -/// # #![feature(provide_any)] -/// use std::any::{Provider, request_value}; -/// -/// fn get_string(provider: &impl Provider) -> String { -/// request_value::(provider).unwrap() -/// } -/// ``` -#[unstable(feature = "provide_any", issue = "96024")] -pub fn request_value<'a, T>(provider: &'a (impl Provider + ?Sized)) -> Option -where - T: 'static, -{ - request_by_type_tag::<'a, tags::Value>(provider) -} - -/// Request a reference from the `Provider`. -/// -/// # Examples -/// -/// Get a string reference from a provider. -/// -/// ```rust -/// # #![feature(provide_any)] -/// use std::any::{Provider, request_ref}; -/// -/// fn get_str(provider: &impl Provider) -> &str { -/// request_ref::(provider).unwrap() -/// } -/// ``` -#[unstable(feature = "provide_any", issue = "96024")] -pub fn request_ref<'a, T>(provider: &'a (impl Provider + ?Sized)) -> Option<&'a T> -where - T: 'static + ?Sized, -{ - request_by_type_tag::<'a, tags::Ref>>(provider) -} - -/// Request a specific value by tag from the `Provider`. -fn request_by_type_tag<'a, I>(provider: &'a (impl Provider + ?Sized)) -> Option -where - I: tags::Type<'a>, -{ - let mut tagged = TaggedOption::<'a, I>(None); - provider.provide(tagged.as_demand()); - tagged.0 -} - -/////////////////////////////////////////////////////////////////////////////// -// Demand and its methods -/////////////////////////////////////////////////////////////////////////////// - -/// A helper object for providing data by type. -/// -/// A data provider provides values by calling this type's provide methods. -#[unstable(feature = "provide_any", issue = "96024")] -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -pub struct Demand<'a>(dyn Erased<'a> + 'a); - -impl<'a> Demand<'a> { - /// Create a new `&mut Demand` from a `&mut dyn Erased` trait object. - fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Demand<'a> { - // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Demand<'a>` is safe since - // `Demand` is repr(transparent). - unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Demand<'a>) } - } - - /// Provide a value or other type with only static lifetimes. - /// - /// # Examples - /// - /// Provides an `u8`. - /// - /// ```rust - /// #![feature(provide_any)] - /// - /// use std::any::{Provider, Demand}; - /// # struct SomeConcreteType { field: u8 } - /// - /// impl Provider for SomeConcreteType { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_value::(self.field); - /// } - /// } - /// ``` - #[unstable(feature = "provide_any", issue = "96024")] - pub fn provide_value(&mut self, value: T) -> &mut Self - where - T: 'static, - { - self.provide::>(value) - } - - /// Provide a value or other type with only static lifetimes computed using a closure. - /// - /// # Examples - /// - /// Provides a `String` by cloning. - /// - /// ```rust - /// #![feature(provide_any)] - /// - /// use std::any::{Provider, Demand}; - /// # struct SomeConcreteType { field: String } - /// - /// impl Provider for SomeConcreteType { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_value_with::(|| self.field.clone()); - /// } - /// } - /// ``` - #[unstable(feature = "provide_any", issue = "96024")] - pub fn provide_value_with(&mut self, fulfil: impl FnOnce() -> T) -> &mut Self - where - T: 'static, - { - self.provide_with::>(fulfil) - } - - /// Provide a reference. The referee type must be bounded by `'static`, - /// but may be unsized. - /// - /// # Examples - /// - /// Provides a reference to a field as a `&str`. - /// - /// ```rust - /// #![feature(provide_any)] - /// - /// use std::any::{Provider, Demand}; - /// # struct SomeConcreteType { field: String } - /// - /// impl Provider for SomeConcreteType { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_ref::(&self.field); - /// } - /// } - /// ``` - #[unstable(feature = "provide_any", issue = "96024")] - pub fn provide_ref(&mut self, value: &'a T) -> &mut Self { - self.provide::>>(value) - } - - /// Provide a reference computed using a closure. The referee type - /// must be bounded by `'static`, but may be unsized. - /// - /// # Examples - /// - /// Provides a reference to a field as a `&str`. - /// - /// ```rust - /// #![feature(provide_any)] - /// - /// use std::any::{Provider, Demand}; - /// # struct SomeConcreteType { business: String, party: String } - /// # fn today_is_a_weekday() -> bool { true } - /// - /// impl Provider for SomeConcreteType { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_ref_with::(|| { - /// if today_is_a_weekday() { - /// &self.business - /// } else { - /// &self.party - /// } - /// }); - /// } - /// } - /// ``` - #[unstable(feature = "provide_any", issue = "96024")] - pub fn provide_ref_with( - &mut self, - fulfil: impl FnOnce() -> &'a T, - ) -> &mut Self { - self.provide_with::>>(fulfil) - } - - /// Provide a value with the given `Type` tag. - fn provide(&mut self, value: I::Reified) -> &mut Self - where - I: tags::Type<'a>, - { - if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { - res.0 = Some(value); - } - self - } - - /// Provide a value with the given `Type` tag, using a closure to prevent unnecessary work. - fn provide_with(&mut self, fulfil: impl FnOnce() -> I::Reified) -> &mut Self - where - I: tags::Type<'a>, - { - if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { - res.0 = Some(fulfil()); - } - self - } - - /// Check if the `Demand` would be satisfied if provided with a - /// value of the specified type. If the type does not match or has - /// already been provided, returns false. - /// - /// # Examples - /// - /// Check if an `u8` still needs to be provided and then provides - /// it. - /// - /// ```rust - /// #![feature(provide_any)] - /// - /// use std::any::{Provider, Demand}; - /// - /// struct Parent(Option); - /// - /// impl Provider for Parent { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// if let Some(v) = self.0 { - /// demand.provide_value::(v); - /// } - /// } - /// } - /// - /// struct Child { - /// parent: Parent, - /// } - /// - /// impl Child { - /// // Pretend that this takes a lot of resources to evaluate. - /// fn an_expensive_computation(&self) -> Option { - /// Some(99) - /// } - /// } - /// - /// impl Provider for Child { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// // In general, we don't know if this call will provide - /// // an `u8` value or not... - /// self.parent.provide(demand); - /// - /// // ...so we check to see if the `u8` is needed before - /// // we run our expensive computation. - /// if demand.would_be_satisfied_by_value_of::() { - /// if let Some(v) = self.an_expensive_computation() { - /// demand.provide_value::(v); - /// } - /// } - /// - /// // The demand will be satisfied now, regardless of if - /// // the parent provided the value or we did. - /// assert!(!demand.would_be_satisfied_by_value_of::()); - /// } - /// } - /// - /// let parent = Parent(Some(42)); - /// let child = Child { parent }; - /// assert_eq!(Some(42), std::any::request_value::(&child)); - /// - /// let parent = Parent(None); - /// let child = Child { parent }; - /// assert_eq!(Some(99), std::any::request_value::(&child)); - /// ``` - #[unstable(feature = "provide_any", issue = "96024")] - pub fn would_be_satisfied_by_value_of(&self) -> bool - where - T: 'static, - { - self.would_be_satisfied_by::>() - } - - /// Check if the `Demand` would be satisfied if provided with a - /// reference to a value of the specified type. If the type does - /// not match or has already been provided, returns false. - /// - /// # Examples - /// - /// Check if a `&str` still needs to be provided and then provides - /// it. - /// - /// ```rust - /// #![feature(provide_any)] - /// - /// use std::any::{Provider, Demand}; - /// - /// struct Parent(Option); - /// - /// impl Provider for Parent { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// if let Some(v) = &self.0 { - /// demand.provide_ref::(v); - /// } - /// } - /// } - /// - /// struct Child { - /// parent: Parent, - /// name: String, - /// } - /// - /// impl Child { - /// // Pretend that this takes a lot of resources to evaluate. - /// fn an_expensive_computation(&self) -> Option<&str> { - /// Some(&self.name) - /// } - /// } - /// - /// impl Provider for Child { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// // In general, we don't know if this call will provide - /// // a `str` reference or not... - /// self.parent.provide(demand); - /// - /// // ...so we check to see if the `&str` is needed before - /// // we run our expensive computation. - /// if demand.would_be_satisfied_by_ref_of::() { - /// if let Some(v) = self.an_expensive_computation() { - /// demand.provide_ref::(v); - /// } - /// } - /// - /// // The demand will be satisfied now, regardless of if - /// // the parent provided the reference or we did. - /// assert!(!demand.would_be_satisfied_by_ref_of::()); - /// } - /// } - /// - /// let parent = Parent(Some("parent".into())); - /// let child = Child { parent, name: "child".into() }; - /// assert_eq!(Some("parent"), std::any::request_ref::(&child)); - /// - /// let parent = Parent(None); - /// let child = Child { parent, name: "child".into() }; - /// assert_eq!(Some("child"), std::any::request_ref::(&child)); - /// ``` - #[unstable(feature = "provide_any", issue = "96024")] - pub fn would_be_satisfied_by_ref_of(&self) -> bool - where - T: ?Sized + 'static, - { - self.would_be_satisfied_by::>>() - } - - fn would_be_satisfied_by(&self) -> bool - where - I: tags::Type<'a>, - { - matches!(self.0.downcast::(), Some(TaggedOption(None))) - } -} - -#[unstable(feature = "provide_any", issue = "96024")] -impl<'a> fmt::Debug for Demand<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Demand").finish_non_exhaustive() - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Type tags -/////////////////////////////////////////////////////////////////////////////// - -mod tags { - //! Type tags are used to identify a type using a separate value. This module includes type tags - //! for some very common types. - //! - //! Currently type tags are not exposed to the user. But in the future, if you want to use the - //! Provider API with more complex types (typically those including lifetime parameters), you - //! will need to write your own tags. - - use crate::marker::PhantomData; - - /// This trait is implemented by specific tag types in order to allow - /// describing a type which can be requested for a given lifetime `'a`. - /// - /// A few example implementations for type-driven tags can be found in this - /// module, although crates may also implement their own tags for more - /// complex types with internal lifetimes. - pub trait Type<'a>: Sized + 'static { - /// The type of values which may be tagged by this tag for the given - /// lifetime. - type Reified: 'a; - } - - /// Similar to the [`Type`] trait, but represents a type which may be unsized (i.e., has a - /// `?Sized` bound). E.g., `str`. - pub trait MaybeSizedType<'a>: Sized + 'static { - type Reified: 'a + ?Sized; - } - - impl<'a, T: Type<'a>> MaybeSizedType<'a> for T { - type Reified = T::Reified; - } - - /// Type-based tag for types bounded by `'static`, i.e., with no borrowed elements. - #[derive(Debug)] - pub struct Value(PhantomData); - - impl<'a, T: 'static> Type<'a> for Value { - type Reified = T; - } - - /// Type-based tag similar to [`Value`] but which may be unsized (i.e., has a `?Sized` bound). - #[derive(Debug)] - pub struct MaybeSizedValue(PhantomData); - - impl<'a, T: ?Sized + 'static> MaybeSizedType<'a> for MaybeSizedValue { - type Reified = T; - } - - /// Type-based tag for reference types (`&'a T`, where T is represented by - /// `>::Reified`. - #[derive(Debug)] - pub struct Ref(PhantomData); - - impl<'a, I: MaybeSizedType<'a>> Type<'a> for Ref { - type Reified = &'a I::Reified; - } -} - -/// An `Option` with a type tag `I`. -/// -/// Since this struct implements `Erased`, the type can be erased to make a dynamically typed -/// option. The type can be checked dynamically using `Erased::tag_id` and since this is statically -/// checked for the concrete type, there is some degree of type safety. -#[repr(transparent)] -struct TaggedOption<'a, I: tags::Type<'a>>(Option); - -impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> { - fn as_demand(&mut self) -> &mut Demand<'a> { - Demand::new(self as &mut (dyn Erased<'a> + 'a)) - } -} - -/// Represents a type-erased but identifiable object. -/// -/// This trait is exclusively implemented by the `TaggedOption` type. -unsafe trait Erased<'a>: 'a { - /// The `TypeId` of the erased type. - fn tag_id(&self) -> TypeId; -} - -unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { - fn tag_id(&self) -> TypeId { - TypeId::of::() - } -} - -#[unstable(feature = "provide_any", issue = "96024")] -impl<'a> dyn Erased<'a> + 'a { - /// Returns some reference to the dynamic value if it is tagged with `I`, - /// or `None` otherwise. - #[inline] - fn downcast(&self) -> Option<&TaggedOption<'a, I>> - where - I: tags::Type<'a>, - { - if self.tag_id() == TypeId::of::() { - // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &*(self as *const Self).cast::>() }) - } else { - None - } - } - - /// Returns some mutable reference to the dynamic value if it is tagged with `I`, - /// or `None` otherwise. - #[inline] - fn downcast_mut(&mut self) -> Option<&mut TaggedOption<'a, I>> - where - I: tags::Type<'a>, - { - if self.tag_id() == TypeId::of::() { - // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &mut *(self as *mut Self).cast::>() }) - } else { - None - } - } -} diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 11cb082757864..c32a54b77ef8e 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -4,8 +4,8 @@ #[cfg(test)] mod tests; -use crate::any::{Demand, Provider, TypeId}; -use crate::fmt::{Debug, Display}; +use crate::any::TypeId; +use crate::fmt::{Debug, Display, Formatter, Result}; /// `Error` is a trait representing the basic expectations for error values, /// i.e., values of type `E` in [`Result`]. @@ -123,16 +123,20 @@ pub trait Error: Debug + Display { /// Provides type based access to context intended for error reports. /// - /// Used in conjunction with [`Demand::provide_value`] and [`Demand::provide_ref`] to extract + /// Used in conjunction with [`Request::provide_value`] and [`Request::provide_ref`] to extract /// references to member variables from `dyn Error` trait objects. /// /// # Example /// /// ```rust - /// #![feature(provide_any)] /// #![feature(error_generic_member_access)] /// use core::fmt; - /// use core::any::Demand; + /// use core::error::{request_ref, Request}; + /// + /// #[derive(Debug)] + /// enum MyLittleTeaPot { + /// Empty, + /// } /// /// #[derive(Debug)] /// struct MyBacktrace { @@ -147,21 +151,7 @@ pub trait Error: Debug + Display { /// } /// /// #[derive(Debug)] - /// struct SourceError { - /// // ... - /// } - /// - /// impl fmt::Display for SourceError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "Example Source Error") - /// } - /// } - /// - /// impl std::error::Error for SourceError {} - /// - /// #[derive(Debug)] /// struct Error { - /// source: SourceError, /// backtrace: MyBacktrace, /// } /// @@ -172,38 +162,26 @@ pub trait Error: Debug + Display { /// } /// /// impl std::error::Error for Error { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand - /// .provide_ref::(&self.backtrace) - /// .provide_ref::(&self.source); + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// request + /// .provide_ref::(&self.backtrace); /// } /// } /// /// fn main() { /// let backtrace = MyBacktrace::new(); - /// let source = SourceError {}; - /// let error = Error { source, backtrace }; + /// let error = Error { backtrace }; /// let dyn_error = &error as &dyn std::error::Error; - /// let backtrace_ref = dyn_error.request_ref::().unwrap(); + /// let backtrace_ref = request_ref::(dyn_error).unwrap(); /// /// assert!(core::ptr::eq(&error.backtrace, backtrace_ref)); + /// assert!(request_ref::(dyn_error).is_none()); /// } /// ``` #[unstable(feature = "error_generic_member_access", issue = "99301")] #[allow(unused_variables)] - fn provide<'a>(&'a self, demand: &mut Demand<'a>) {} -} - -#[unstable(feature = "error_generic_member_access", issue = "99301")] -impl Provider for E -where - E: Error + ?Sized, -{ - fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - self.provide(demand) - } + fn provide<'a>(&'a self, request: &mut Request<'a>) {} } - mod private { // This is a hack to prevent `type_id` from being overridden by `Error` // implementations, since that can enable unsound downcasting. @@ -215,20 +193,6 @@ mod private { #[unstable(feature = "never_type", issue = "35121")] impl Error for ! {} -impl<'a> dyn Error + 'a { - /// Request a reference of type `T` as context about this error. - #[unstable(feature = "error_generic_member_access", issue = "99301")] - pub fn request_ref(&'a self) -> Option<&'a T> { - core::any::request_ref(self) - } - - /// Request a value of type `T` as context about this error. - #[unstable(feature = "error_generic_member_access", issue = "99301")] - pub fn request_value(&'a self) -> Option { - core::any::request_value(self) - } -} - // Copied from `any.rs`. impl dyn Error + 'static { /// Returns `true` if the inner type is the same as `T`. @@ -293,18 +257,6 @@ impl dyn Error + 'static + Send { pub fn downcast_mut(&mut self) -> Option<&mut T> { ::downcast_mut::(self) } - - /// Request a reference of type `T` as context about this error. - #[unstable(feature = "error_generic_member_access", issue = "99301")] - pub fn request_ref(&self) -> Option<&T> { - ::request_ref(self) - } - - /// Request a value of type `T` as context about this error. - #[unstable(feature = "error_generic_member_access", issue = "99301")] - pub fn request_value(&self) -> Option { - ::request_value(self) - } } impl dyn Error + 'static + Send + Sync { @@ -328,18 +280,6 @@ impl dyn Error + 'static + Send + Sync { pub fn downcast_mut(&mut self) -> Option<&mut T> { ::downcast_mut::(self) } - - /// Request a reference of type `T` as context about this error. - #[unstable(feature = "error_generic_member_access", issue = "99301")] - pub fn request_ref(&self) -> Option<&T> { - ::request_ref(self) - } - - /// Request a value of type `T` as context about this error. - #[unstable(feature = "error_generic_member_access", issue = "99301")] - pub fn request_value(&self) -> Option { - ::request_value(self) - } } impl dyn Error { @@ -412,6 +352,645 @@ impl dyn Error { } } +/// Request a value of type `T` from the given `impl Error`. +/// +/// # Examples +/// +/// Get a string value from an error. +/// +/// ```rust +/// # #![feature(error_generic_member_access)] +/// use std::error::Error; +/// use core::error::request_value; +/// +/// fn get_string(err: &impl Error) -> String { +/// request_value::(err).unwrap() +/// } +/// ``` +#[unstable(feature = "error_generic_member_access", issue = "99301")] +pub fn request_value<'a, T>(err: &'a (impl Error + ?Sized)) -> Option +where + T: 'static, +{ + request_by_type_tag::<'a, tags::Value>(err) +} + +/// Request a reference of type `T` from the given `impl Error`. +/// +/// # Examples +/// +/// Get a string reference from an error. +/// +/// ```rust +/// # #![feature(error_generic_member_access)] +/// use core::error::Error; +/// use core::error::request_ref; +/// +/// fn get_str(err: &impl Error) -> &str { +/// request_ref::(err).unwrap() +/// } +/// ``` +#[unstable(feature = "error_generic_member_access", issue = "99301")] +pub fn request_ref<'a, T>(err: &'a (impl Error + ?Sized)) -> Option<&'a T> +where + T: 'static + ?Sized, +{ + request_by_type_tag::<'a, tags::Ref>>(err) +} + +/// Request a specific value by tag from the `Error`. +fn request_by_type_tag<'a, I>(err: &'a (impl Error + ?Sized)) -> Option +where + I: tags::Type<'a>, +{ + let mut tagged = TaggedOption::<'a, I>(None); + err.provide(tagged.as_request()); + tagged.0 +} + +/////////////////////////////////////////////////////////////////////////////// +// Request and its methods +/////////////////////////////////////////////////////////////////////////////// + +/// `Request` supports generic, type-driven access to data. It's use is currently restricted to the +/// standard library in cases where trait authors wish to allow trait implementors to share generic +/// information across trait boundaries. The motivating and prototypical use case is +/// `core::error::Error` which would otherwise require a method per concrete type (eg. +/// `std::backtrace::Backtrace` instance that implementors want to expose to users). +/// +/// # Data flow +/// +/// To describe the intended data flow for Request objects, let's consider two conceptual users +/// separated by API boundaries: +/// +/// * Consumer - the consumer requests objects using a Request instance; eg a crate that offers +/// fancy `Error`/`Result` reporting to users wants to request a Backtrace from a given `dyn Error`. +/// +/// * Producer - the producer provides objects when requested via Request; eg. a library with an +/// an `Error` implementation that automatically captures backtraces at the time instances are +/// created. +/// +/// The consumer only needs to know where to submit their request and are expected to handle the +/// request not being fulfilled by the use of `Option` in the responses offered by the producer. +/// +/// * A Producer initializes the value of one of its fields of a specific type. (or is otherwise +/// prepared to generate a value requested). eg, `backtrace::Backtrace` or +/// `std::backtrace::Backtrace` +/// * A Consumer requests an object of a specific type (say `std::backtrace::Backtrace). In the case +/// of a `dyn Error` trait object (the Producer), there are methods called `request_ref` and +/// `request_value` are available to simplify obtaining an ``Option`` for a given type. * The +/// Producer, when requested, populates the given Request object which is given as a mutable +/// reference. +/// * The Consumer extracts a value or reference to the requested type from the `Request` object +/// wrapped in an `Option`; in the case of `dyn Error` the aforementioned `request_ref` and ` +/// request_value` methods mean that `dyn Error` users don't have to deal with the `Request` type at +/// all (but `Error` implementors do). The `None` case of the `Option` suggests only that the +/// Producer cannot currently offer an instance of the requested type, not it can't or never will. +/// +/// # Examples +/// +/// The best way to demonstrate this is using an example implementation of `Error`'s `provide` trait +/// method: +/// +/// ``` +/// #![feature(error_generic_member_access)] +/// use core::fmt; +/// use core::error::Request; +/// use core::error::request_ref; +/// +/// #[derive(Debug)] +/// enum MyLittleTeaPot { +/// Empty, +/// } +/// +/// #[derive(Debug)] +/// struct MyBacktrace { +/// // ... +/// } +/// +/// impl MyBacktrace { +/// fn new() -> MyBacktrace { +/// // ... +/// # MyBacktrace {} +/// } +/// } +/// +/// #[derive(Debug)] +/// struct Error { +/// backtrace: MyBacktrace, +/// } +/// +/// impl fmt::Display for Error { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "Example Error") +/// } +/// } +/// +/// impl std::error::Error for Error { +/// fn provide<'a>(&'a self, request: &mut Request<'a>) { +/// request +/// .provide_ref::(&self.backtrace); +/// } +/// } +/// +/// fn main() { +/// let backtrace = MyBacktrace::new(); +/// let error = Error { backtrace }; +/// let dyn_error = &error as &dyn std::error::Error; +/// let backtrace_ref = request_ref::(dyn_error).unwrap(); +/// +/// assert!(core::ptr::eq(&error.backtrace, backtrace_ref)); +/// assert!(request_ref::(dyn_error).is_none()); +/// } +/// ``` +/// +#[unstable(feature = "error_generic_member_access", issue = "99301")] +#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 +pub struct Request<'a>(dyn Erased<'a> + 'a); + +impl<'a> Request<'a> { + /// Create a new `&mut Request` from a `&mut dyn Erased` trait object. + fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Request<'a> { + // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Request<'a>` is safe since + // `Request` is repr(transparent). + unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Request<'a>) } + } + + /// Provide a value or other type with only static lifetimes. + /// + /// # Examples + /// + /// Provides an `u8`. + /// + /// ```rust + /// #![feature(error_generic_member_access)] + /// + /// use core::error::Request; + /// + /// #[derive(Debug)] + /// struct SomeConcreteType { field: u8 } + /// + /// impl std::fmt::Display for SomeConcreteType { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "{} failed", self.field) + /// } + /// } + /// + /// impl std::error::Error for SomeConcreteType { + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// request.provide_value::(self.field); + /// } + /// } + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn provide_value(&mut self, value: T) -> &mut Self + where + T: 'static, + { + self.provide::>(value) + } + + /// Provide a value or other type with only static lifetimes computed using a closure. + /// + /// # Examples + /// + /// Provides a `String` by cloning. + /// + /// ```rust + /// #![feature(error_generic_member_access)] + /// + /// use core::error::Request; + /// + /// #[derive(Debug)] + /// struct SomeConcreteType { field: String } + /// + /// impl std::fmt::Display for SomeConcreteType { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "{} failed", self.field) + /// } + /// } + /// + /// impl std::error::Error for SomeConcreteType { + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// request.provide_value_with::(|| self.field.clone()); + /// } + /// } + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn provide_value_with(&mut self, fulfil: impl FnOnce() -> T) -> &mut Self + where + T: 'static, + { + self.provide_with::>(fulfil) + } + + /// Provide a reference. The referee type must be bounded by `'static`, + /// but may be unsized. + /// + /// # Examples + /// + /// Provides a reference to a field as a `&str`. + /// + /// ```rust + /// #![feature(error_generic_member_access)] + /// + /// use core::error::Request; + /// + /// #[derive(Debug)] + /// struct SomeConcreteType { field: String } + /// + /// impl std::fmt::Display for SomeConcreteType { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "{} failed", self.field) + /// } + /// } + /// + /// impl std::error::Error for SomeConcreteType { + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// request.provide_ref::(&self.field); + /// } + /// } + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn provide_ref(&mut self, value: &'a T) -> &mut Self { + self.provide::>>(value) + } + + /// Provide a reference computed using a closure. The referee type + /// must be bounded by `'static`, but may be unsized. + /// + /// # Examples + /// + /// Provides a reference to a field as a `&str`. + /// + /// ```rust + /// #![feature(error_generic_member_access)] + /// + /// use core::error::Request; + /// + /// #[derive(Debug)] + /// struct SomeConcreteType { business: String, party: String } + /// fn today_is_a_weekday() -> bool { true } + /// + /// impl std::fmt::Display for SomeConcreteType { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "{} failed", self.business) + /// } + /// } + /// + /// impl std::error::Error for SomeConcreteType { + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// request.provide_ref_with::(|| { + /// if today_is_a_weekday() { + /// &self.business + /// } else { + /// &self.party + /// } + /// }); + /// } + /// } + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn provide_ref_with( + &mut self, + fulfil: impl FnOnce() -> &'a T, + ) -> &mut Self { + self.provide_with::>>(fulfil) + } + + /// Provide a value with the given `Type` tag. + fn provide(&mut self, value: I::Reified) -> &mut Self + where + I: tags::Type<'a>, + { + if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { + res.0 = Some(value); + } + self + } + + /// Provide a value with the given `Type` tag, using a closure to prevent unnecessary work. + fn provide_with(&mut self, fulfil: impl FnOnce() -> I::Reified) -> &mut Self + where + I: tags::Type<'a>, + { + if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { + res.0 = Some(fulfil()); + } + self + } + + /// Check if the `Request` would be satisfied if provided with a + /// value of the specified type. If the type does not match or has + /// already been provided, returns false. + /// + /// # Examples + /// + /// Check if an `u8` still needs to be provided and then provides + /// it. + /// + /// ```rust + /// #![feature(error_generic_member_access)] + /// + /// use core::error::Request; + /// use core::error::request_value; + /// + /// #[derive(Debug)] + /// struct Parent(Option); + /// + /// impl std::fmt::Display for Parent { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "a parent failed") + /// } + /// } + /// + /// impl std::error::Error for Parent { + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// if let Some(v) = self.0 { + /// request.provide_value::(v); + /// } + /// } + /// } + /// + /// #[derive(Debug)] + /// struct Child { + /// parent: Parent, + /// } + /// + /// impl Child { + /// // Pretend that this takes a lot of resources to evaluate. + /// fn an_expensive_computation(&self) -> Option { + /// Some(99) + /// } + /// } + /// + /// impl std::fmt::Display for Child { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "child failed: \n because of parent: {}", self.parent) + /// } + /// } + /// + /// impl std::error::Error for Child { + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// // In general, we don't know if this call will provide + /// // an `u8` value or not... + /// self.parent.provide(request); + /// + /// // ...so we check to see if the `u8` is needed before + /// // we run our expensive computation. + /// if request.would_be_satisfied_by_value_of::() { + /// if let Some(v) = self.an_expensive_computation() { + /// request.provide_value::(v); + /// } + /// } + /// + /// // The request will be satisfied now, regardless of if + /// // the parent provided the value or we did. + /// assert!(!request.would_be_satisfied_by_value_of::()); + /// } + /// } + /// + /// let parent = Parent(Some(42)); + /// let child = Child { parent }; + /// assert_eq!(Some(42), request_value::(&child)); + /// + /// let parent = Parent(None); + /// let child = Child { parent }; + /// assert_eq!(Some(99), request_value::(&child)); + /// + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn would_be_satisfied_by_value_of(&self) -> bool + where + T: 'static, + { + self.would_be_satisfied_by::>() + } + + /// Check if the `Request` would be satisfied if provided with a + /// reference to a value of the specified type. If the type does + /// not match or has already been provided, returns false. + /// + /// # Examples + /// + /// Check if a `&str` still needs to be provided and then provides + /// it. + /// + /// ```rust + /// #![feature(error_generic_member_access)] + /// + /// use core::error::Request; + /// use core::error::request_ref; + /// + /// #[derive(Debug)] + /// struct Parent(Option); + /// + /// impl std::fmt::Display for Parent { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "a parent failed") + /// } + /// } + /// + /// impl std::error::Error for Parent { + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// if let Some(v) = &self.0 { + /// request.provide_ref::(v); + /// } + /// } + /// } + /// + /// #[derive(Debug)] + /// struct Child { + /// parent: Parent, + /// name: String, + /// } + /// + /// impl Child { + /// // Pretend that this takes a lot of resources to evaluate. + /// fn an_expensive_computation(&self) -> Option<&str> { + /// Some(&self.name) + /// } + /// } + /// + /// impl std::fmt::Display for Child { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "{} failed: \n {}", self.name, self.parent) + /// } + /// } + /// + /// impl std::error::Error for Child { + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// // In general, we don't know if this call will provide + /// // a `str` reference or not... + /// self.parent.provide(request); + /// + /// // ...so we check to see if the `&str` is needed before + /// // we run our expensive computation. + /// if request.would_be_satisfied_by_ref_of::() { + /// if let Some(v) = self.an_expensive_computation() { + /// request.provide_ref::(v); + /// } + /// } + /// + /// // The request will be satisfied now, regardless of if + /// // the parent provided the reference or we did. + /// assert!(!request.would_be_satisfied_by_ref_of::()); + /// } + /// } + /// + /// let parent = Parent(Some("parent".into())); + /// let child = Child { parent, name: "child".into() }; + /// assert_eq!(Some("parent"), request_ref::(&child)); + /// + /// let parent = Parent(None); + /// let child = Child { parent, name: "child".into() }; + /// assert_eq!(Some("child"), request_ref::(&child)); + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "99301")] + pub fn would_be_satisfied_by_ref_of(&self) -> bool + where + T: ?Sized + 'static, + { + self.would_be_satisfied_by::>>() + } + + fn would_be_satisfied_by(&self) -> bool + where + I: tags::Type<'a>, + { + matches!(self.0.downcast::(), Some(TaggedOption(None))) + } +} + +#[unstable(feature = "error_generic_member_access", issue = "99301")] +impl<'a> Debug for Request<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_struct("Request").finish_non_exhaustive() + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Type tags +/////////////////////////////////////////////////////////////////////////////// + +pub(crate) mod tags { + //! Type tags are used to identify a type using a separate value. This module includes type tags + //! for some very common types. + //! + //! Currently type tags are not exposed to the user. But in the future, if you want to use the + //! Request API with more complex types (typically those including lifetime parameters), you + //! will need to write your own tags. + + use crate::marker::PhantomData; + + /// This trait is implemented by specific tag types in order to allow + /// describing a type which can be requested for a given lifetime `'a`. + /// + /// A few example implementations for type-driven tags can be found in this + /// module, although crates may also implement their own tags for more + /// complex types with internal lifetimes. + pub(crate) trait Type<'a>: Sized + 'static { + /// The type of values which may be tagged by this tag for the given + /// lifetime. + type Reified: 'a; + } + + /// Similar to the [`Type`] trait, but represents a type which may be unsized (i.e., has a + /// `?Sized` bound). E.g., `str`. + pub(crate) trait MaybeSizedType<'a>: Sized + 'static { + type Reified: 'a + ?Sized; + } + + impl<'a, T: Type<'a>> MaybeSizedType<'a> for T { + type Reified = T::Reified; + } + + /// Type-based tag for types bounded by `'static`, i.e., with no borrowed elements. + #[derive(Debug)] + pub(crate) struct Value(PhantomData); + + impl<'a, T: 'static> Type<'a> for Value { + type Reified = T; + } + + /// Type-based tag similar to [`Value`] but which may be unsized (i.e., has a `?Sized` bound). + #[derive(Debug)] + pub(crate) struct MaybeSizedValue(PhantomData); + + impl<'a, T: ?Sized + 'static> MaybeSizedType<'a> for MaybeSizedValue { + type Reified = T; + } + + /// Type-based tag for reference types (`&'a T`, where T is represented by + /// `>::Reified`. + #[derive(Debug)] + pub(crate) struct Ref(PhantomData); + + impl<'a, I: MaybeSizedType<'a>> Type<'a> for Ref { + type Reified = &'a I::Reified; + } +} + +/// An `Option` with a type tag `I`. +/// +/// Since this struct implements `Erased`, the type can be erased to make a dynamically typed +/// option. The type can be checked dynamically using `Erased::tag_id` and since this is statically +/// checked for the concrete type, there is some degree of type safety. +#[repr(transparent)] +pub(crate) struct TaggedOption<'a, I: tags::Type<'a>>(pub Option); + +impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> { + pub(crate) fn as_request(&mut self) -> &mut Request<'a> { + Request::new(self as &mut (dyn Erased<'a> + 'a)) + } +} + +/// Represents a type-erased but identifiable object. +/// +/// This trait is exclusively implemented by the `TaggedOption` type. +unsafe trait Erased<'a>: 'a { + /// The `TypeId` of the erased type. + fn tag_id(&self) -> TypeId; +} + +unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { + fn tag_id(&self) -> TypeId { + TypeId::of::() + } +} + +impl<'a> dyn Erased<'a> + 'a { + /// Returns some reference to the dynamic value if it is tagged with `I`, + /// or `None` otherwise. + #[inline] + fn downcast(&self) -> Option<&TaggedOption<'a, I>> + where + I: tags::Type<'a>, + { + if self.tag_id() == TypeId::of::() { + // SAFETY: Just checked whether we're pointing to an I. + Some(unsafe { &*(self as *const Self).cast::>() }) + } else { + None + } + } + + /// Returns some mutable reference to the dynamic value if it is tagged with `I`, + /// or `None` otherwise. + #[inline] + fn downcast_mut(&mut self) -> Option<&mut TaggedOption<'a, I>> + where + I: tags::Type<'a>, + { + if self.tag_id() == TypeId::of::() { + // SAFETY: Just checked whether we're pointing to an I. + Some(unsafe { &mut *(self as *mut Self).cast::>() }) + } else { + None + } + } +} + /// An iterator over an [`Error`] and its sources. /// /// If you want to omit the initial error and only process @@ -449,8 +1028,8 @@ impl<'a, T: Error + ?Sized> Error for &'a T { Error::source(&**self) } - fn provide<'b>(&'b self, demand: &mut Demand<'b>) { - Error::provide(&**self, demand); + fn provide<'b>(&'b self, request: &mut Request<'b>) { + Error::provide(&**self, request); } } diff --git a/library/core/tests/any.rs b/library/core/tests/any.rs index a8f6b7ebb9250..8d2d31b64310f 100644 --- a/library/core/tests/any.rs +++ b/library/core/tests/any.rs @@ -147,65 +147,3 @@ fn dyn_type_name() { std::any::type_name:: + Send + Sync>() ); } - -// Test the `Provider` API. - -struct SomeConcreteType { - some_string: String, -} - -impl Provider for SomeConcreteType { - fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - demand - .provide_ref::(&self.some_string) - .provide_ref::(&self.some_string) - .provide_value_with::(|| "bye".to_owned()); - } -} - -// Test the provide and request mechanisms with a by-reference trait object. -#[test] -fn test_provider() { - let obj: &dyn Provider = &SomeConcreteType { some_string: "hello".to_owned() }; - - assert_eq!(&**request_ref::(obj).unwrap(), "hello"); - assert_eq!(&*request_value::(obj).unwrap(), "bye"); - assert_eq!(request_value::(obj), None); -} - -// Test the provide and request mechanisms with a boxed trait object. -#[test] -fn test_provider_boxed() { - let obj: Box = Box::new(SomeConcreteType { some_string: "hello".to_owned() }); - - assert_eq!(&**request_ref::(&*obj).unwrap(), "hello"); - assert_eq!(&*request_value::(&*obj).unwrap(), "bye"); - assert_eq!(request_value::(&*obj), None); -} - -// Test the provide and request mechanisms with a concrete object. -#[test] -fn test_provider_concrete() { - let obj = SomeConcreteType { some_string: "hello".to_owned() }; - - assert_eq!(&**request_ref::(&obj).unwrap(), "hello"); - assert_eq!(&*request_value::(&obj).unwrap(), "bye"); - assert_eq!(request_value::(&obj), None); -} - -trait OtherTrait: Provider {} - -impl OtherTrait for SomeConcreteType {} - -impl dyn OtherTrait { - fn get_ref(&self) -> Option<&T> { - request_ref::(self) - } -} - -// Test the provide and request mechanisms via an intermediate trait. -#[test] -fn test_provider_intermediate() { - let obj: &dyn OtherTrait = &SomeConcreteType { some_string: "hello".to_owned() }; - assert_eq!(obj.get_ref::().unwrap(), "hello"); -} diff --git a/library/core/tests/error.rs b/library/core/tests/error.rs new file mode 100644 index 0000000000000..cb7cb5441d1d8 --- /dev/null +++ b/library/core/tests/error.rs @@ -0,0 +1,66 @@ +use core::error::{request_value, request_ref, Request}; + +// Test the `Request` API. +#[derive(Debug)] +struct SomeConcreteType { + some_string: String, +} + +impl std::fmt::Display for SomeConcreteType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "A") + } +} + +impl std::error::Error for SomeConcreteType { + fn provide<'a>(&'a self, request: &mut Request<'a>) { + request + .provide_ref::(&self.some_string) + .provide_ref::(&self.some_string) + .provide_value_with::(|| "bye".to_owned()); + } +} + +// Test the Error.provide and request mechanisms with a by-reference trait object. +#[test] +fn test_error_generic_member_access() { + let obj = &SomeConcreteType { some_string: "hello".to_owned() }; + + assert_eq!(request_ref::(&*obj).unwrap(), "hello"); + assert_eq!(request_value::(&*obj).unwrap(), "bye"); + assert_eq!(request_value::(&obj), None); +} + +// Test the Error.provide and request mechanisms with a by-reference trait object. +#[test] +fn test_request_constructor() { + let obj: &dyn std::error::Error = &SomeConcreteType { some_string: "hello".to_owned() }; + + assert_eq!(request_ref::(&*obj).unwrap(), "hello"); + assert_eq!(request_value::(&*obj).unwrap(), "bye"); + assert_eq!(request_value::(&obj), None); +} + +// Test the Error.provide and request mechanisms with a boxed trait object. +#[test] +fn test_error_generic_member_access_boxed() { + let obj: Box = + Box::new(SomeConcreteType { some_string: "hello".to_owned() }); + + assert_eq!(request_ref::(&*obj).unwrap(), "hello"); + assert_eq!(request_value::(&*obj).unwrap(), "bye"); + + // NOTE: Box only implements Error when E: Error + Sized, which means we can't pass a + // Box to request_value. + //assert_eq!(request_value::(&obj).unwrap(), "bye"); +} + +// Test the Error.provide and request mechanisms with a concrete object. +#[test] +fn test_error_generic_member_access_concrete() { + let obj = SomeConcreteType { some_string: "hello".to_owned() }; + + assert_eq!(request_ref::(&obj).unwrap(), "hello"); + assert_eq!(request_value::(&obj).unwrap(), "bye"); + assert_eq!(request_value::(&obj), None); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 897a5e9b87063..e5517991682f3 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -105,7 +105,9 @@ #![feature(const_slice_from_ref)] #![feature(waker_getters)] #![feature(slice_flatten)] -#![feature(provide_any)] +#![feature(error_generic_member_access)] +#![feature(error_in_core)] +#![feature(trait_upcasting)] #![feature(utf8_chunks)] #![feature(is_ascii_octdigit)] #![feature(get_many_mut)] diff --git a/library/std/src/error.rs b/library/std/src/error.rs index ee5eddebfaf7c..7bc3af1793e36 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -9,6 +9,8 @@ use crate::fmt::{self, Write}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; +#[unstable(feature = "error_generic_member_access", issue = "99301")] +pub use core::error::{request_ref, Request}; mod private { // This is a hack to prevent `type_id` from being overridden by `Error` @@ -371,11 +373,10 @@ impl Report { /// /// ```rust /// #![feature(error_reporter)] - /// #![feature(provide_any)] /// #![feature(error_generic_member_access)] /// # use std::error::Error; /// # use std::fmt; - /// use std::any::Demand; + /// use std::error::Request; /// use std::error::Report; /// use std::backtrace::Backtrace; /// @@ -405,8 +406,8 @@ impl Report { /// } /// /// impl Error for SuperErrorSideKick { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_ref::(&self.backtrace); + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// request.provide_ref::(&self.backtrace); /// } /// } /// @@ -459,11 +460,11 @@ where fn backtrace(&self) -> Option<&Backtrace> { // have to grab the backtrace on the first error directly since that error may not be // 'static - let backtrace = (&self.error as &dyn Error).request_ref(); + let backtrace = request_ref(&self.error); let backtrace = backtrace.or_else(|| { self.error .source() - .map(|source| source.sources().find_map(|source| source.request_ref())) + .map(|source| source.sources().find_map(|source| request_ref(source))) .flatten() }); backtrace diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index ee999bd65c3c9..ed070a26b0cf0 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -1,6 +1,6 @@ use super::Error; use crate::fmt; -use core::any::Demand; +use core::error::Request; #[derive(Debug, PartialEq)] struct A; @@ -199,7 +199,7 @@ where self.source.as_deref() } - fn provide<'a>(&'a self, req: &mut Demand<'a>) { + fn provide<'a>(&'a self, req: &mut Request<'a>) { self.backtrace.as_ref().map(|bt| req.provide_ref::(bt)); } } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6f58e5a0f0534..c07aa5cd91fee 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -306,7 +306,6 @@ #![feature(pointer_is_aligned)] #![feature(portable_simd)] #![feature(prelude_2024)] -#![feature(provide_any)] #![feature(ptr_as_uninit)] #![feature(raw_os_nonzero)] #![feature(round_ties_even)]