From a5762625a168f195afbc5b74d674a93f8c692a8e Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Fri, 24 Apr 2015 14:34:57 -0700 Subject: [PATCH] Add downcasting to std::error::Error This commit brings the `Error` trait in line with the [Error interoperation RFC](https://github.com/rust-lang/rfcs/pull/201) by adding downcasting, which has long been intended. This change means that for any `Error` trait objects that are `'static`, you can downcast to concrete error types. To make this work, it is necessary for `Error` to inherit from `Reflect` (which is currently used to mark concrete types as "permitted for reflection, aka downcasting"). This is a breaking change: it means that impls like ```rust impl Error for MyErrorType { ... } ``` must change to something like ```rust impl Error for MyErrorType { ... } ``` except that `Reflect` is currently unstable (and should remain so for the time being). For now, code can instead bound by `Any`: ```rust impl Error for MyErrorType { ... } ``` which *is* stable and has `Reflect` as a super trait. The downside is that this imposes a `'static` constraint, but that only constrains *when* `Error` is implemented -- it does not actually constrain the types that can implement `Error`. [breaking-change] --- src/liballoc/boxed.rs | 11 ++- src/libcore/any.rs | 6 +- src/libcore/marker.rs | 2 + src/libstd/error.rs | 130 ++++++++++++++++++++++++++++++-- src/libstd/io/buffered.rs | 3 +- src/libstd/sync/mpsc/mod.rs | 6 +- src/libstd/sys/common/poison.rs | 7 +- 7 files changed, 146 insertions(+), 19 deletions(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 7696abd659f..7a089d733cf 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -240,6 +240,7 @@ impl Hash for Box { impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] + /// Attempt to downcast the box to a concrete type. pub fn downcast(self) -> Result, Box> { if self.is::() { unsafe { @@ -257,11 +258,15 @@ impl Box { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn downcast(self) -> Result, Box> { - >::downcast(self) + /// Attempt to downcast the box to a concrete type. + pub fn downcast(self) -> Result, Box> { + >::downcast(self).map_err(|s| unsafe { + // reapply the Send marker + mem::transmute::, Box>(s) + }) } } diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 85b8accadf3..a65394f5268 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -97,9 +97,7 @@ pub trait Any: Reflect + 'static { fn get_type_id(&self) -> TypeId; } -impl Any for T - where T: Reflect + 'static -{ +impl Any for T { fn get_type_id(&self) -> TypeId { TypeId::of::() } } @@ -222,7 +220,7 @@ impl TypeId { /// Returns the `TypeId` of the type this generic function has been /// instantiated with #[stable(feature = "rust1", since = "1.0.0")] - pub fn of() -> TypeId { + pub fn of() -> TypeId { TypeId { t: unsafe { intrinsics::type_id::() }, } diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 44e5390098b..e8c7045141d 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -416,6 +416,8 @@ mod impls { #[rustc_reflect_like] #[unstable(feature = "core", reason = "requires RFC and more experience")] #[allow(deprecated)] +#[rustc_on_unimplemented = "`{Self}` does not implement `Any`; \ + ensure all type parameters are bounded by `Any`"] pub trait Reflect {} impl Reflect for .. { } diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 9f09f464cfc..06e4d69818e 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -47,19 +47,22 @@ // coherence challenge (e.g., specialization, neg impls, etc) we can // reconsider what crate these items belong in. -use boxed::Box; +use any::TypeId; +use boxed::{self, Box}; use convert::From; use fmt::{self, Debug, Display}; -use marker::{Send, Sync}; +use marker::{Send, Sync, Reflect}; +use mem::transmute; use num; -use option::Option; -use option::Option::None; +use option::Option::{self, Some, None}; +use result::Result::{self, Ok, Err}; +use raw::TraitObject; use str; use string::{self, String}; /// Base functionality for all errors in Rust. #[stable(feature = "rust1", since = "1.0.0")] -pub trait Error: Debug + Display { +pub trait Error: Debug + Display + Reflect { /// A short description of the error. /// /// The description should not contain newlines or sentence-ending @@ -71,6 +74,14 @@ pub trait Error: Debug + Display { /// The lower-level cause of this error, if any. #[stable(feature = "rust1", since = "1.0.0")] fn cause(&self) -> Option<&Error> { None } + + /// Get the `TypeId` of `self` + #[doc(hidden)] + #[unstable(feature = "core", + reason = "unclear whether to commit to this public implementation detail")] + fn type_id(&self) -> TypeId where Self: 'static { + TypeId::of::() + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -154,3 +165,112 @@ impl Error for string::FromUtf16Error { } } +// copied from any.rs +impl Error + 'static { + /// Returns true if the boxed type is the same as `T` + #[unstable(feature = "error_downcast", reason = "recently added")] + #[inline] + pub fn is(&self) -> bool { + // Get TypeId of the type this function is instantiated with + let t = TypeId::of::(); + + // Get TypeId of the type in the trait object + let boxed = self.type_id(); + + // Compare both TypeIds on equality + t == boxed + } + + /// Returns some reference to the boxed value if it is of type `T`, or + /// `None` if it isn't. + #[unstable(feature = "error_downcast", reason = "recently added")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + if self.is::() { + unsafe { + // Get the raw representation of the trait object + let to: TraitObject = transmute(self); + + // Extract the data pointer + Some(transmute(to.data)) + } + } else { + None + } + } + + /// Returns some mutable reference to the boxed value if it is of type `T`, or + /// `None` if it isn't. + #[unstable(feature = "error_downcast", reason = "recently added")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.is::() { + unsafe { + // Get the raw representation of the trait object + let to: TraitObject = transmute(self); + + // Extract the data pointer + Some(transmute(to.data)) + } + } else { + None + } + } +} + +impl Error + 'static + Send { + /// Forwards to the method defined on the type `Any`. + #[unstable(feature = "error_downcast", reason = "recently added")] + #[inline] + pub fn is(&self) -> bool { + ::is::(self) + } + + /// Forwards to the method defined on the type `Any`. + #[unstable(feature = "error_downcast", reason = "recently added")] + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + ::downcast_ref::(self) + } + + /// Forwards to the method defined on the type `Any`. + #[unstable(feature = "error_downcast", reason = "recently added")] + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + ::downcast_mut::(self) + } +} + +impl Error { + #[inline] + #[unstable(feature = "error_downcast", reason = "recently added")] + /// Attempt to downcast the box to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + if self.is::() { + unsafe { + // Get the raw representation of the trait object + let raw = boxed::into_raw(self); + let to: TraitObject = + transmute::<*mut Error, TraitObject>(raw); + + // Extract the data pointer + Ok(Box::from_raw(to.data as *mut T)) + } + } else { + Err(self) + } + } +} + +impl Error + Send { + #[inline] + #[unstable(feature = "error_downcast", reason = "recently added")] + /// Attempt to downcast the box to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + let err: Box = self; + ::downcast(err).map_err(|s| unsafe { + // reapply the Send marker + transmute::, Box>(s) + }) + } +} diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index ed6023b2b81..897ac673182 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -13,6 +13,7 @@ use prelude::v1::*; use io::prelude::*; +use marker::Reflect; use cmp; use error; use fmt; @@ -322,7 +323,7 @@ impl From> for Error { } #[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for IntoInnerError { +impl error::Error for IntoInnerError { fn description(&self) -> &str { error::Error::description(self.error()) } diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index 61932225d79..965ad74fb60 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -272,6 +272,7 @@ use error; use fmt; use mem; use cell::UnsafeCell; +use marker::Reflect; pub use self::select::{Select, Handle}; use self::select::StartResult; @@ -955,8 +956,7 @@ impl fmt::Display for SendError { } #[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for SendError { - +impl error::Error for SendError { fn description(&self) -> &str { "sending on a closed channel" } @@ -991,7 +991,7 @@ impl fmt::Display for TrySendError { } #[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for TrySendError { +impl error::Error for TrySendError { fn description(&self) -> &str { match *self { diff --git a/src/libstd/sys/common/poison.rs b/src/libstd/sys/common/poison.rs index 6deb4a48007..6c59231c23a 100644 --- a/src/libstd/sys/common/poison.rs +++ b/src/libstd/sys/common/poison.rs @@ -10,6 +10,7 @@ use prelude::v1::*; +use marker::Reflect; use cell::UnsafeCell; use error::{Error}; use fmt; @@ -109,7 +110,7 @@ impl fmt::Display for PoisonError { } } -impl Error for PoisonError { +impl Error for PoisonError { fn description(&self) -> &str { "poisoned lock: another task failed inside" } @@ -155,13 +156,13 @@ impl fmt::Debug for TryLockError { } #[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for TryLockError { +impl fmt::Display for TryLockError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.description().fmt(f) } } -impl Error for TryLockError { +impl Error for TryLockError { fn description(&self) -> &str { match *self { TryLockError::Poisoned(ref p) => p.description(),