Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add downcasting to std::error::Error #24793

Merged
merged 1 commit into from
May 1, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/liballoc/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ impl<T: ?Sized + Hash> Hash for Box<T> {
impl Box<Any> {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
/// Attempt to downcast the box to a concrete type.
pub fn downcast<T: Any>(self) -> Result<Box<T>, Box<Any>> {
if self.is::<T>() {
unsafe {
Expand All @@ -257,11 +258,15 @@ impl Box<Any> {
}
}

impl Box<Any+Send> {
impl Box<Any + Send> {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn downcast<T: Any>(self) -> Result<Box<T>, Box<Any>> {
<Box<Any>>::downcast(self)
/// Attempt to downcast the box to a concrete type.
pub fn downcast<T: Any>(self) -> Result<Box<T>, Box<Any + Send>> {
<Box<Any>>::downcast(self).map_err(|s| unsafe {
// reapply the Send marker
mem::transmute::<Box<Any>, Box<Any + Send>>(s)
})
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/libcore/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ pub trait Any: Reflect + 'static {
fn get_type_id(&self) -> TypeId;
}

impl<T> Any for T
where T: Reflect + 'static
{
impl<T: Reflect + 'static> Any for T {
fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}

Expand Down Expand Up @@ -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<T: ?Sized + Any>() -> TypeId {
pub fn of<T: ?Sized + Reflect + 'static>() -> TypeId {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to just use Any here, the docs will be clearer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to use Reflect here to allow Error to implement downcasting without inheriting from Any.

TypeId {
t: unsafe { intrinsics::type_id::<T>() },
}
Expand Down
2 changes: 2 additions & 0 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 .. { }
130 changes: 125 additions & 5 deletions src/libstd/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you move the Reflect bound to a method-level where clause here, then we can use exclusively Any and avoid stabilizing Reflect.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, actually this will prevent downcasting with Box<Error>, rather than Box<Error + Reflect>, which iirc isn't even allowed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drat, it was an interesting idea!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought about this some more, and we can get away without stabilizing Reflect with just a bit less flexibility - instead of implementing Error for T: Reflect in e.g. TryLockError, just implement for T: Any. It's slightly less flexible, but it's backwards compatible to generalize the impls when/if we do stabilize Reflect directly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@reem Ah yes, I think at some point in the iterations on this topic I confused myself into thinking this would hurt more than it does: you can still have non-'static generic error types, they will just fail to be Error for the time being. That seems plausible, and I agree that not stabilizing Reflect is a good idea.

TypeId::of::<Self>()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -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<T: Error + 'static>(&self) -> bool {
// Get TypeId of the type this function is instantiated with
let t = TypeId::of::<T>();

// 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<T: Error + 'static>(&self) -> Option<&T> {
if self.is::<T>() {
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<T: Error + 'static>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
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<T: Error + 'static>(&self) -> bool {
<Error + 'static>::is::<T>(self)
}

/// Forwards to the method defined on the type `Any`.
#[unstable(feature = "error_downcast", reason = "recently added")]
#[inline]
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
<Error + 'static>::downcast_ref::<T>(self)
}

/// Forwards to the method defined on the type `Any`.
#[unstable(feature = "error_downcast", reason = "recently added")]
#[inline]
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
<Error + 'static>::downcast_mut::<T>(self)
}
}

impl Error {
#[inline]
#[unstable(feature = "error_downcast", reason = "recently added")]
/// Attempt to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Error>> {
if self.is::<T>() {
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<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Error + Send>> {
let err: Box<Error> = self;
<Error>::downcast(err).map_err(|s| unsafe {
// reapply the Send marker
transmute::<Box<Error>, Box<Error + Send>>(s)
})
}
}
3 changes: 2 additions & 1 deletion src/libstd/io/buffered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use prelude::v1::*;
use io::prelude::*;

use marker::Reflect;
use cmp;
use error;
use fmt;
Expand Down Expand Up @@ -322,7 +323,7 @@ impl<W> From<IntoInnerError<W>> for Error {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> {
impl<W: Reflect + Send + fmt::Debug> error::Error for IntoInnerError<W> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this and the bounds used below change from Reflect to Any? (just to reflect idiomatic usage)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have mixed feelings about this one. I decided to leave them as Reflect to avoid the 'static restriction, but I don't feel too strongly either way. (FWIW, in the long run we will either stabilize Reflect, making this possible, or remove Reflect, making it irrelevant.)

fn description(&self) -> &str {
error::Error::description(self.error())
}
Expand Down
6 changes: 3 additions & 3 deletions src/libstd/sync/mpsc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -955,8 +956,7 @@ impl<T> fmt::Display for SendError<T> {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Send> error::Error for SendError<T> {

impl<T: Send + Reflect> error::Error for SendError<T> {
fn description(&self) -> &str {
"sending on a closed channel"
}
Expand Down Expand Up @@ -991,7 +991,7 @@ impl<T> fmt::Display for TrySendError<T> {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Send> error::Error for TrySendError<T> {
impl<T: Send + Reflect> error::Error for TrySendError<T> {

fn description(&self) -> &str {
match *self {
Expand Down
7 changes: 4 additions & 3 deletions src/libstd/sys/common/poison.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use prelude::v1::*;

use marker::Reflect;
use cell::UnsafeCell;
use error::{Error};
use fmt;
Expand Down Expand Up @@ -109,7 +110,7 @@ impl<T> fmt::Display for PoisonError<T> {
}
}

impl<T: Send> Error for PoisonError<T> {
impl<T: Send + Reflect> Error for PoisonError<T> {
fn description(&self) -> &str {
"poisoned lock: another task failed inside"
}
Expand Down Expand Up @@ -155,13 +156,13 @@ impl<T> fmt::Debug for TryLockError<T> {
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Send> fmt::Display for TryLockError<T> {
impl<T: Send + Reflect> fmt::Display for TryLockError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}

impl<T: Send> Error for TryLockError<T> {
impl<T: Send + Reflect> Error for TryLockError<T> {
fn description(&self) -> &str {
match *self {
TryLockError::Poisoned(ref p) => p.description(),
Expand Down