From dec394bc1345863bcc81a47bd392a44a6d09e376 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 20 Sep 2016 11:24:24 +0000 Subject: [PATCH] Rust: port std::error into libstd_artiq. See https://github.com/jethrogb/rust-core_io/issues/3. --- artiq/runtime.rs/libstd_artiq/error.rs | 453 ++++++++++++++++++++++ artiq/runtime.rs/libstd_artiq/io/error.rs | 54 ++- artiq/runtime.rs/libstd_artiq/lib.rs | 4 +- 3 files changed, 501 insertions(+), 10 deletions(-) create mode 100644 artiq/runtime.rs/libstd_artiq/error.rs diff --git a/artiq/runtime.rs/libstd_artiq/error.rs b/artiq/runtime.rs/libstd_artiq/error.rs new file mode 100644 index 000000000..df1d76b6c --- /dev/null +++ b/artiq/runtime.rs/libstd_artiq/error.rs @@ -0,0 +1,453 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Traits for working with Errors. +//! +//! # The `Error` trait +//! +//! `Error` is a trait representing the basic expectations for error values, +//! i.e. values of type `E` in `Result`. At a minimum, errors must provide +//! a description, but they may optionally provide additional detail (via +//! `Display`) and cause chain information: +//! +//! ``` +//! use std::fmt::Display; +//! +//! trait Error: Display { +//! fn description(&self) -> &str; +//! +//! fn cause(&self) -> Option<&Error> { None } +//! } +//! ``` +//! +//! The `cause` method is generally used when errors cross "abstraction +//! boundaries", i.e. when a one module must report an error that is "caused" +//! by an error from a lower-level module. This setup makes it possible for the +//! high-level module to provide its own errors that do not commit to any +//! particular implementation, but also reveal some of its implementation for +//! debugging via `cause` chains. + +// A note about crates and the facade: +// +// Originally, the `Error` trait was defined in libcore, and the impls +// were scattered about. However, coherence objected to this +// arrangement, because to create the blanket impls for `Box` required +// knowing that `&str: !Error`, and we have no means to deal with that +// sort of conflict just now. Therefore, for the time being, we have +// moved the `Error` trait into libstd. As we evolve a sol'n to the +// coherence challenge (e.g., specialization, neg impls, etc) we can +// reconsider what crate these items belong in. + +use any::TypeId; +use boxed::Box; +use cell; +use fmt::{self, Debug, Display}; +use marker::{Send, Sync, Reflect}; +use mem::transmute; +use num; +use core::raw::TraitObject; +use str; +use string::{self, String}; + +/// Base functionality for all errors in Rust. +pub trait Error: Debug + Display + Reflect { + /// A short description of the error. + /// + /// The description should not contain newlines or sentence-ending + /// punctuation, to facilitate embedding in larger user-facing + /// strings. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// + /// match "xc".parse::() { + /// Err(e) => { + /// println!("Error: {}", e.description()); + /// } + /// _ => println!("No error"), + /// } + /// ``` + fn description(&self) -> &str; + + /// The lower-level cause of this error, if any. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct SuperError { + /// side: SuperErrorSideKick, + /// } + /// + /// impl fmt::Display for SuperError { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "SuperError is here!") + /// } + /// } + /// + /// impl Error for SuperError { + /// fn description(&self) -> &str { + /// "I'm the superhero of errors!" + /// } + /// + /// fn cause(&self) -> Option<&Error> { + /// Some(&self.side) + /// } + /// } + /// + /// #[derive(Debug)] + /// struct SuperErrorSideKick; + /// + /// impl fmt::Display for SuperErrorSideKick { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "SuperErrorSideKick is here!") + /// } + /// } + /// + /// impl Error for SuperErrorSideKick { + /// fn description(&self) -> &str { + /// "I'm SuperError side kick!" + /// } + /// } + /// + /// fn get_super_error() -> Result<(), SuperError> { + /// Err(SuperError { side: SuperErrorSideKick }) + /// } + /// + /// fn main() { + /// match get_super_error() { + /// Err(e) => { + /// println!("Error: {}", e.description()); + /// println!("Caused by: {}", e.cause().unwrap()); + /// } + /// _ => println!("No error"), + /// } + /// } + /// ``` + fn cause(&self) -> Option<&Error> { None } + + /// Get the `TypeId` of `self` + #[doc(hidden)] + fn type_id(&self) -> TypeId where Self: 'static { + TypeId::of::() + } +} + +impl<'a, E: Error + 'a> From for Box { + fn from(err: E) -> Box { + Box::new(err) + } +} + +impl<'a, E: Error + Send + Sync + 'a> From for Box { + fn from(err: E) -> Box { + Box::new(err) + } +} + +impl From for Box { + fn from(err: String) -> Box { + #[derive(Debug)] + struct StringError(String); + + impl Error for StringError { + fn description(&self) -> &str { &self.0 } + } + + impl Display for StringError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } + } + + Box::new(StringError(err)) + } +} + +impl From for Box { + fn from(str_err: String) -> Box { + let err1: Box = From::from(str_err); + let err2: Box = err1; + err2 + } +} + +impl<'a, 'b> From<&'b str> for Box { + fn from(err: &'b str) -> Box { + From::from(String::from(err)) + } +} + +impl<'a> From<&'a str> for Box { + fn from(err: &'a str) -> Box { + From::from(String::from(err)) + } +} + +impl Error for str::ParseBoolError { + fn description(&self) -> &str { "failed to parse bool" } +} + +impl Error for str::Utf8Error { + fn description(&self) -> &str { + "invalid utf-8: corrupt contents" + } +} + +impl Error for num::ParseIntError { + fn description(&self) -> &str { + self.__description() + } +} + +impl Error for num::TryFromIntError { + fn description(&self) -> &str { + self.__description() + } +} + +impl Error for num::ParseFloatError { + fn description(&self) -> &str { + self.__description() + } +} + +impl Error for string::FromUtf8Error { + fn description(&self) -> &str { + "invalid utf-8" + } +} + +impl Error for string::FromUtf16Error { + fn description(&self) -> &str { + "invalid utf-16" + } +} + +impl Error for string::ParseError { + fn description(&self) -> &str { + match *self {} + } +} + +impl Error for Box { + fn description(&self) -> &str { + Error::description(&**self) + } + + fn cause(&self) -> Option<&Error> { + Error::cause(&**self) + } +} + +impl Error for fmt::Error { + fn description(&self) -> &str { + "an error occurred when formatting an argument" + } +} + +impl<'a, T: ?Sized + Reflect> Error for cell::BorrowError<'a, T> { + fn description(&self) -> &str { + "already mutably borrowed" + } +} + +impl<'a, T: ?Sized + Reflect> Error for cell::BorrowMutError<'a, T> { + fn description(&self) -> &str { + "already borrowed" + } +} + +// copied from any.rs +impl Error + 'static { + /// Returns true if the boxed type is the same as `T` + #[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. + #[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(&*(to.data as *const T)) + } + } else { + None + } + } + + /// Returns some mutable reference to the boxed value if it is of type `T`, or + /// `None` if it isn't. + #[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(&mut *(to.data as *const T as *mut T)) + } + } else { + None + } + } +} + +impl Error + 'static + Send { + /// Forwards to the method defined on the type `Any`. + #[inline] + pub fn is(&self) -> bool { + ::is::(self) + } + + /// Forwards to the method defined on the type `Any`. + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + ::downcast_ref::(self) + } + + /// Forwards to the method defined on the type `Any`. + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + ::downcast_mut::(self) + } +} + +impl Error + 'static + Send + Sync { + /// Forwards to the method defined on the type `Any`. + #[inline] + pub fn is(&self) -> bool { + ::is::(self) + } + + /// Forwards to the method defined on the type `Any`. + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + ::downcast_ref::(self) + } + + /// Forwards to the method defined on the type `Any`. + #[inline] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + ::downcast_mut::(self) + } +} + +impl Error { + #[inline] + /// 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 = Box::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] + /// 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) + }) + } +} + +impl Error + Send + Sync { + #[inline] + /// 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+Sync marker + transmute::, Box>(s) + }) + } +} + +#[cfg(test)] +mod tests { + use prelude::v1::*; + use super::Error; + use fmt; + + #[derive(Debug, PartialEq)] + struct A; + #[derive(Debug, PartialEq)] + struct B; + + impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "A") + } + } + impl fmt::Display for B { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "B") + } + } + + impl Error for A { + fn description(&self) -> &str { "A-desc" } + } + impl Error for B { + fn description(&self) -> &str { "A-desc" } + } + + #[test] + fn downcasting() { + let mut a = A; + let mut a = &mut a as &mut (Error + 'static); + assert_eq!(a.downcast_ref::(), Some(&A)); + assert_eq!(a.downcast_ref::(), None); + assert_eq!(a.downcast_mut::(), Some(&mut A)); + assert_eq!(a.downcast_mut::(), None); + + let a: Box = Box::new(A); + match a.downcast::() { + Ok(..) => panic!("expected error"), + Err(e) => assert_eq!(*e.downcast::().unwrap(), A), + } + } +} diff --git a/artiq/runtime.rs/libstd_artiq/io/error.rs b/artiq/runtime.rs/libstd_artiq/io/error.rs index bdb040c03..f8684a4ec 100644 --- a/artiq/runtime.rs/libstd_artiq/io/error.rs +++ b/artiq/runtime.rs/libstd_artiq/io/error.rs @@ -14,7 +14,7 @@ use core::fmt; use core::marker::{Send, Sync}; use core::option::Option::{self, Some, None}; use core::result; - use collections::string::String; +use error; /// A specialized [`Result`](../result/enum.Result.html) type for I/O /// operations. @@ -68,7 +68,7 @@ enum Repr { #[derive(Debug)] struct Custom { kind: ErrorKind, - error: String, + error: Box, } /// A list specifying general categories of I/O error. @@ -163,12 +163,12 @@ impl Error { /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); /// ``` pub fn new(kind: ErrorKind, error: E) -> Error - where E: Into + where E: Into> { Self::_new(kind, error.into()) } - fn _new(kind: ErrorKind, error: String) -> Error { + fn _new(kind: ErrorKind, error: Box) -> Error { Error { repr: Repr::Custom(Box::new(Custom { kind: kind, @@ -198,10 +198,10 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. - pub fn get_ref(&self) -> Option<&String> { + pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> { match self.repr { Repr::Os(..) => None, - Repr::Custom(ref c) => Some(&c.error), + Repr::Custom(ref c) => Some(&*c.error), } } @@ -210,10 +210,10 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. - pub fn get_mut(&mut self) -> Option<&mut String> { + pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> { match self.repr { Repr::Os(..) => None, - Repr::Custom(ref mut c) => Some(&mut c.error), + Repr::Custom(ref mut c) => Some(&mut *c.error), } } @@ -221,7 +221,7 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. - pub fn into_inner(self) -> Option { + pub fn into_inner(self) -> Option> { match self.repr { Repr::Os(..) => None, Repr::Custom(c) => Some(c.error) @@ -258,6 +258,42 @@ impl fmt::Display for Error { } } +impl error::Error for Error { + fn description(&self) -> &str { + match self.repr { + Repr::Os(..) => match self.kind() { + ErrorKind::NotFound => "entity not found", + ErrorKind::PermissionDenied => "permission denied", + ErrorKind::ConnectionRefused => "connection refused", + ErrorKind::ConnectionReset => "connection reset", + ErrorKind::ConnectionAborted => "connection aborted", + ErrorKind::NotConnected => "not connected", + ErrorKind::AddrInUse => "address in use", + ErrorKind::AddrNotAvailable => "address not available", + ErrorKind::BrokenPipe => "broken pipe", + ErrorKind::AlreadyExists => "entity already exists", + ErrorKind::WouldBlock => "operation would block", + ErrorKind::InvalidInput => "invalid input parameter", + ErrorKind::InvalidData => "invalid data", + ErrorKind::TimedOut => "timed out", + ErrorKind::WriteZero => "write zero", + ErrorKind::Interrupted => "operation interrupted", + ErrorKind::Other => "other os error", + ErrorKind::UnexpectedEof => "unexpected end of file", + ErrorKind::__Nonexhaustive => unreachable!() + }, + Repr::Custom(ref c) => c.error.description(), + } + } + + fn cause(&self) -> Option<&error::Error> { + match self.repr { + Repr::Os(..) => None, + Repr::Custom(ref c) => c.error.cause(), + } + } +} + fn _assert_error_is_sync_send() { fn _is_sync_send() {} _is_sync_send::(); diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index 5c4abf384..1ec357302 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -1,5 +1,6 @@ #![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime, - question_mark, unicode)] + question_mark, unicode, reflect_marker, raw, int_error_internals, + try_from, try_borrow)] #![no_std] #![needs_panic_runtime] @@ -27,6 +28,7 @@ pub mod prelude { } pub mod time; +pub mod error; pub mod io; use core::fmt::Write;