Prepared `std::sys` for removal, and made `begin_unwind` simpler

- `begin_unwind` is now generic over any `T: Any + Send`.
- Every value you fail with gets boxed as an `~Any`.
- Because of implementation details, `&'static str` and `~str` are still
  handled specially behind the scenes.
- Changed the big macro source string in libsyntax to a raw string
  literal, and enabled doc comments there.
This commit is contained in:
Marvin Löbel 2013-10-27 20:12:40 +01:00
parent e42e378f32
commit 54f4dcd76a
16 changed files with 187 additions and 229 deletions

View File

@ -118,6 +118,8 @@ pub mod extra {
pub use std::clone;
pub use std::condition;
pub use std::cmp;
// NOTE: Remove import after next snapshot
#[cfg(stage0)]
pub use std::sys;
pub use std::unstable;
pub use std::str;

View File

@ -65,7 +65,7 @@ unsafe fn fail_borrowed(box: *mut raw::Box<()>, file: *c_char, line: size_t) ->
None => { // not recording borrows
let msg = "borrowed";
do msg.with_c_str |msg_p| {
task::begin_unwind(msg_p, file, line);
task::begin_unwind_raw(msg_p, file, line);
}
}
Some(borrow_list) => { // recording borrows
@ -81,7 +81,7 @@ unsafe fn fail_borrowed(box: *mut raw::Box<()>, file: *c_char, line: size_t) ->
}
}
do msg.with_c_str |msg_p| {
task::begin_unwind(msg_p, file, line)
task::begin_unwind_raw(msg_p, file, line)
}
}
}
@ -180,7 +180,7 @@ pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint,
if br.box != a || br.file != file || br.line != line {
let err = format!("wrong borrow found, br={:?}", br);
do err.with_c_str |msg_p| {
task::begin_unwind(msg_p, file, line)
task::begin_unwind_raw(msg_p, file, line)
}
}
borrow_list

View File

@ -155,7 +155,7 @@ use cell::Cell;
use option::{Option, Some, None};
use prelude::*;
use rt::task::Task;
use rt::task::UnwindReasonLinked;
use rt::task::UnwindMessageLinked;
use rt::task::{UnwindResult, Failure};
use task::spawn::Taskgroup;
use to_bytes::IterBytes;
@ -597,7 +597,7 @@ impl Death {
}
if !success {
result = Cell::new(Failure(UnwindReasonLinked));
result = Cell::new(Failure(UnwindMessageLinked));
}
}
on_exit(result.take());

View File

@ -95,8 +95,8 @@ pub enum UnwindResult {
/// The task is ending successfully
Success,
/// The Task is failing with reason `UnwindReason`
Failure(UnwindReason),
/// The Task is failing with reason `UnwindMessage`
Failure(UnwindMessage),
}
impl UnwindResult {
@ -121,20 +121,25 @@ impl UnwindResult {
/// Represents the cause of a task failure
#[deriving(ToStr)]
pub enum UnwindReason {
/// Failed with a string message
UnwindReasonStr(SendStr),
pub enum UnwindMessage {
// FIXME: #9913 - This variant is not neccessary once Any works properly
/// Failed with a static string message
UnwindMessageStrStatic(&'static str),
// FIXME: #9913 - This variant is not neccessary once Any works properly
/// Failed with a owned string message
UnwindMessageStrOwned(~str),
/// Failed with an `~Any`
UnwindReasonAny(~Any),
UnwindMessageAny(~Any),
/// Failed because of linked failure
UnwindReasonLinked
UnwindMessageLinked
}
pub struct Unwinder {
unwinding: bool,
cause: Option<UnwindReason>
cause: Option<UnwindMessage>
}
impl Unwinder {
@ -527,7 +532,7 @@ impl Unwinder {
}
}
pub fn begin_unwind(&mut self, cause: UnwindReason) -> ! {
pub fn begin_unwind(&mut self, cause: UnwindMessage) -> ! {
#[fixed_stack_segment]; #[inline(never)];
self.unwinding = true;
@ -622,7 +627,7 @@ pub extern "C" fn rust_stack_exhausted() {
/// This is the entry point of unwinding for things like lang items and such.
/// The arguments are normally generated by the compiler, and need to
/// have static lifetimes.
pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! {
pub fn begin_unwind_raw(msg: *c_char, file: *c_char, line: size_t) -> ! {
use c_str::CString;
use cast::transmute;
@ -638,11 +643,33 @@ pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! {
let msg = static_char_ptr(msg);
let file = static_char_ptr(file);
begin_unwind_reason(UnwindReasonStr(msg.into_send_str()), file, line as uint)
begin_unwind(msg, file, line as uint)
}
/// This is the entry point of unwinding for fail!() and assert!().
pub fn begin_unwind_reason(reason: UnwindReason, file: &'static str, line: uint) -> ! {
pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
// Wrap the fail message in a `Any` box for uniform representation.
let any = ~msg as ~Any;
// FIXME: #9913 - This can be changed to be internal to begin_unwind_internal
// once Any works properly.
// As a workaround, string types need to be special cased right now
// because `Any` does not support dynamically querying whether the
// type implements a trait yet, so without requiring that every `Any`
// also implements `ToStr` there is no way to get a failure message
// out of it again during unwinding.
let msg = if any.is::<&'static str>() {
UnwindMessageStrStatic(*any.move::<&'static str>().unwrap())
} else if any.is::<~str>() {
UnwindMessageStrOwned(*any.move::<~str>().unwrap())
} else {
UnwindMessageAny(any)
};
begin_unwind_internal(msg, file, line)
}
fn begin_unwind_internal(msg: UnwindMessage, file: &'static str, line: uint) -> ! {
use rt::in_green_task_context;
use rt::task::Task;
use rt::local::Local;
@ -656,15 +683,16 @@ pub fn begin_unwind_reason(reason: UnwindReason, file: &'static str, line: uint)
let task: *mut Task;
{
let msg = match reason {
UnwindReasonStr(ref s) => s.as_slice(),
UnwindReasonAny(_) => "~Any",
UnwindReasonLinked => "linked failure",
let msg_s = match msg {
UnwindMessageAny(_) => "~Any",
UnwindMessageLinked => "linked failure",
UnwindMessageStrOwned(ref s) => s.as_slice(),
UnwindMessageStrStatic(ref s) => s.as_slice(),
};
if !in_green_task_context() {
rterrln!("failed in non-task context at '{}', {}:{}",
msg, file, line);
msg_s, file, line);
intrinsics::abort();
}
@ -679,19 +707,20 @@ pub fn begin_unwind_reason(reason: UnwindReason, file: &'static str, line: uint)
// due to mismanagment of its own kill flag, so calling our own
// logger in its current state is a bit of a problem.
rterrln!("task '{}' failed at '{}', {}:{}", n, msg, file, line);
rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s, file, line);
if (*task).unwinder.unwinding {
rtabort!("unwinding again");
}
}
(*task).unwinder.begin_unwind(reason);
(*task).unwinder.begin_unwind(msg);
}
}
#[cfg(test)]
mod test {
use super::*;
use rt::test::*;
#[test]
@ -804,4 +833,8 @@ mod test {
a.next = Some(b);
}
}
#[test]
#[should_fail]
fn test_begin_unwind() { begin_unwind("cause", file!(), line!()) }
}

View File

@ -184,6 +184,8 @@ pub mod os;
pub mod path;
pub mod rand;
pub mod run;
// NOTE: Remove module after next snapshot
#[cfg(stage0)]
pub mod sys;
pub mod cast;
pub mod fmt;
@ -226,7 +228,10 @@ mod std {
pub use logging;
pub use option;
pub use os;
pub use rt;
pub use str;
// NOTE: Remove import after next snapshot
#[cfg(stage0)]
pub use sys;
pub use to_bytes;
pub use to_str;

View File

@ -10,99 +10,20 @@
//! Misc low level stuff
// NOTE: Remove this module after an snapshot
#[allow(missing_doc)];
use any::Any;
use kinds::Send;
use rt::task::{UnwindReasonStr, UnwindReasonAny};
use rt::task;
use send_str::{SendStr, IntoSendStr};
/// Trait for initiating task failure with a sendable cause.
pub trait FailWithCause {
/// Fail the current task with `cause`.
fn fail_with(cause: Self, file: &'static str, line: uint) -> !;
}
impl FailWithCause for ~str {
fn fail_with(cause: ~str, file: &'static str, line: uint) -> ! {
task::begin_unwind_reason(UnwindReasonStr(cause.into_send_str()), file, line)
impl<T: Any + Send> FailWithCause for T {
fn fail_with(msg: T, file: &'static str, line: uint) -> ! {
task::begin_unwind(msg, file, line)
}
}
impl FailWithCause for &'static str {
fn fail_with(cause: &'static str, file: &'static str, line: uint) -> ! {
task::begin_unwind_reason(UnwindReasonStr(cause.into_send_str()), file, line)
}
}
impl FailWithCause for SendStr {
fn fail_with(cause: SendStr, file: &'static str, line: uint) -> ! {
task::begin_unwind_reason(UnwindReasonStr(cause), file, line)
}
}
impl FailWithCause for ~Any {
fn fail_with(cause: ~Any, file: &'static str, line: uint) -> ! {
task::begin_unwind_reason(UnwindReasonAny(cause), file, line)
}
}
impl<T: Any + Send + 'static> FailWithCause for ~T {
fn fail_with(cause: ~T, file: &'static str, line: uint) -> ! {
task::begin_unwind_reason(UnwindReasonAny(cause as ~Any), file, line)
}
}
#[cfg(test)]
mod tests {
use super::*;
use any::Any;
use cast;
use send_str::IntoSendStr;
#[test]
fn synthesize_closure() {
use unstable::raw::Closure;
unsafe {
let x = 10;
let f: &fn(int) -> int = |y| x + y;
assert_eq!(f(20), 30);
let original_closure: Closure = cast::transmute(f);
let actual_function_pointer = original_closure.code;
let environment = original_closure.env;
let new_closure = Closure {
code: actual_function_pointer,
env: environment
};
let new_f: &fn(int) -> int = cast::transmute(new_closure);
assert_eq!(new_f(20), 30);
}
}
#[test]
#[should_fail]
fn fail_static() { FailWithCause::fail_with("cause", file!(), line!()) }
#[test]
#[should_fail]
fn fail_owned() { FailWithCause::fail_with(~"cause", file!(), line!()) }
#[test]
#[should_fail]
fn fail_send() { FailWithCause::fail_with("cause".into_send_str(), file!(), line!()) }
#[test]
#[should_fail]
fn fail_any() { FailWithCause::fail_with(~612_u16 as ~Any, file!(), line!()) }
#[test]
#[should_fail]
fn fail_any_wrap() { FailWithCause::fail_with(~413_u16, file!(), line!()) }
}

View File

@ -60,7 +60,8 @@ use comm::{stream, Chan, GenericChan, GenericPort, Port, Peekable};
use result::{Result, Ok, Err};
use rt::in_green_task_context;
use rt::local::Local;
use rt::task::{UnwindReasonAny, UnwindReasonLinked, UnwindReasonStr};
use rt::task::{UnwindMessageAny, UnwindMessageLinked};
use rt::task::{UnwindMessageStrStatic, UnwindMessageStrOwned};
use rt::task::{UnwindResult, Success, Failure};
use send_str::{SendStr, IntoSendStr};
use unstable::finally::Finally;
@ -93,9 +94,10 @@ pub struct LinkedFailure;
fn wrap_as_any(res: UnwindResult) -> TaskResult {
match res {
Success => Ok(()),
Failure(UnwindReasonStr(s)) => Err(~s as ~Any),
Failure(UnwindReasonAny(a)) => Err(a),
Failure(UnwindReasonLinked) => Err(~LinkedFailure as ~Any)
Failure(UnwindMessageAny(a)) => Err(a),
Failure(UnwindMessageLinked) => Err(~LinkedFailure as ~Any),
Failure(UnwindMessageStrOwned(s)) => Err(~s as ~Any),
Failure(UnwindMessageStrStatic(s)) => Err(~s as ~Any),
}
}
@ -1425,38 +1427,52 @@ fn test_indestructible() {
}
#[test]
fn test_try_fail_cause_static_str() {
fn test_try_fail_message_static_str() {
match do try {
fail!("static string");
} {
Err(ref e) if e.is::<SendStr>() => {}
Err(_) | Ok(()) => fail!()
Err(e) => {
type T = &'static str;
assert!(e.is::<T>());
assert_eq!(*e.move::<T>().unwrap(), "static string");
}
Ok(()) => fail!()
}
}
#[test]
fn test_try_fail_cause_owned_str() {
fn test_try_fail_message_owned_str() {
match do try {
fail!(~"owned string");
} {
Err(ref e) if e.is::<SendStr>() => {}
Err(_) | Ok(()) => fail!()
Err(e) => {
type T = ~str;
assert!(e.is::<T>());
assert_eq!(*e.move::<T>().unwrap(), ~"owned string");
}
Ok(()) => fail!()
}
}
#[test]
fn test_try_fail_cause_any() {
fn test_try_fail_message_any() {
match do try {
fail!(~413u16 as ~Any);
} {
Err(ref e) if e.is::<u16>() => {}
Err(_) | Ok(()) => fail!()
Err(e) => {
type T = ~Any;
assert!(e.is::<T>());
let any = e.move::<T>().unwrap();
assert!(any.is::<u16>());
assert_eq!(*any.move::<u16>().unwrap(), 413u16);
}
Ok(()) => fail!()
}
}
#[ignore(reason = "linked failure")]
#[test]
fn test_try_fail_cause_linked() {
fn test_try_fail_message_linked() {
match do try {
do spawn {
fail!()
@ -1468,11 +1484,11 @@ fn test_try_fail_cause_linked() {
}
#[test]
fn test_try_fail_cause_any_wrapped() {
fn test_try_fail_message_unit_struct() {
struct Juju;
match do try {
fail!(~Juju)
fail!(Juju)
} {
Err(ref e) if e.is::<Juju>() => {}
Err(_) | Ok(()) => fail!()

View File

@ -83,12 +83,11 @@ use local_data;
use rt::local::Local;
use rt::sched::{Scheduler, Shutdown, TaskFromFriend};
use rt::task::{Task, Sched};
use rt::task::{UnwindReasonLinked, UnwindReasonStr};
use rt::task::{UnwindMessageLinked, UnwindMessageStrStatic};
use rt::task::{UnwindResult, Success, Failure};
use rt::thread::Thread;
use rt::work_queue::WorkQueue;
use rt::{in_green_task_context, new_event_loop, KillHandle};
use send_str::IntoSendStr;
use task::SingleThreaded;
use task::TaskOpts;
use task::unkillable;
@ -325,7 +324,7 @@ impl Drop for Taskgroup {
do RuntimeGlue::with_task_handle_and_failing |me, failing| {
if failing {
for x in self.notifier.mut_iter() {
x.task_result = Some(Failure(UnwindReasonLinked));
x.task_result = Some(Failure(UnwindMessageLinked));
}
// Take everybody down with us. After this point, every
// other task in the group will see 'tg' as none, which
@ -380,7 +379,7 @@ impl AutoNotify {
notify_chan: chan,
// Un-set above when taskgroup successfully made.
task_result: Some(Failure(UnwindReasonStr("AutoNotify::new()".into_send_str())))
task_result: Some(Failure(UnwindMessageStrStatic("AutoNotify::new()")))
}
}
}

View File

@ -19,7 +19,7 @@ use rt::borrowck;
#[cold]
#[lang="fail_"]
pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! {
task::begin_unwind(expr, file, line);
task::begin_unwind_raw(expr, file, line);
}
#[cold]

View File

@ -62,3 +62,33 @@ impl Repr<*Box<String>> for @str {}
// sure would be nice to have this
// impl<T> Repr<*Vec<T>> for ~[T] {}
#[cfg(test)]
mod tests {
use super::*;
use cast;
#[test]
fn synthesize_closure() {
unsafe {
let x = 10;
let f: &fn(int) -> int = |y| x + y;
assert_eq!(f(20), 30);
let original_closure: Closure = cast::transmute(f);
let actual_function_pointer = original_closure.code;
let environment = original_closure.env;
let new_closure = Closure {
code: actual_function_pointer,
env: environment
};
let new_f: &fn(int) -> int = cast::transmute(new_closure);
assert_eq!(new_f(20), 30);
}
}
}

View File

@ -604,9 +604,9 @@ impl AstBuilder for @ExtCtxt {
span,
~[
self.ident_of("std"),
self.ident_of("sys"),
self.ident_of("FailWithCause"),
self.ident_of("fail_with"),
self.ident_of("rt"),
self.ident_of("task"),
self.ident_of("begin_unwind"),
],
~[
self.expr_str(span, msg),

View File

@ -763,8 +763,7 @@ pub fn new_span(cx: @ExtCtxt, sp: Span) -> Span {
// syntax elements.
pub fn std_macros() -> @str {
return
@"mod __std_macros {
@r#"mod __std_macros {
#[macro_escape];
#[doc(hidden)];
@ -789,31 +788,30 @@ pub fn std_macros() -> @str {
macro_rules! fail(
() => (
fail!(\"explicit failure\")
fail!("explicit failure")
);
($fmt:expr) => (
::std::sys::FailWithCause::fail_with($fmt, file!(), line!())
($msg:expr) => (
::std::rt::task::begin_unwind($msg, file!(), line!())
);
($fmt:expr, $($arg:tt)*) => (
::std::sys::FailWithCause::fail_with(format!($fmt, $($arg)*), file!(), line!())
::std::rt::task::begin_unwind(format!($fmt, $($arg)*), file!(), line!())
)
)
macro_rules! assert(
($cond:expr) => {
if !$cond {
::std::sys::FailWithCause::fail_with(
\"assertion failed: \" + stringify!($cond), file!(), line!())
fail!("assertion failed: {:s}", stringify!($cond))
}
};
($cond:expr, $msg:expr) => {
if !$cond {
::std::sys::FailWithCause::fail_with($msg, file!(), line!())
fail!($msg)
}
};
($cond:expr, $( $arg:expr ),+) => {
if !$cond {
::std::sys::FailWithCause::fail_with(format!( $($arg),+ ), file!(), line!())
fail!( $($arg),+ )
}
}
)
@ -826,9 +824,8 @@ pub fn std_macros() -> @str {
// check both directions of equality....
if !((*given_val == *expected_val) &&
(*expected_val == *given_val)) {
fail!(\"assertion failed: `(left == right) && (right == \
left)` (left: `{:?}`, right: `{:?}`)\",
*given_val, *expected_val);
fail!("assertion failed: `(left == right) && (right == left)` \
(left: `{:?}`, right: `{:?}`)", *given_val, *expected_val)
}
}
)
@ -846,7 +843,7 @@ pub fn std_macros() -> @str {
given_val.approx_eq(&expected_val) &&
expected_val.approx_eq(&given_val)
) {
fail!(\"left: {:?} does not approximately equal right: {:?}\",
fail!("left: {:?} does not approximately equal right: {:?}",
given_val, expected_val);
}
}
@ -863,42 +860,37 @@ pub fn std_macros() -> @str {
given_val.approx_eq_eps(&expected_val, &epsilon_val) &&
expected_val.approx_eq_eps(&given_val, &epsilon_val)
) {
fail!(\"left: {:?} does not approximately equal right: \
{:?} with epsilon: {:?}\",
fail!("left: {:?} does not approximately equal right: \
{:?} with epsilon: {:?}",
given_val, expected_val, epsilon_val);
}
}
)
)
// FIXME(#6266): change the /* to /** when attributes are supported on macros
// (Though even then—is it going to work according to the clear intent here?)
/*
A utility macro for indicating unreachable code. It will fail if
executed. This is occasionally useful to put after loops that never
terminate normally, but instead directly return from a function.
# Example
```rust
fn choose_weighted_item(v: &[Item]) -> Item {
assert!(!v.is_empty());
let mut so_far = 0u;
for v.each |item| {
so_far += item.weight;
if so_far > 100 {
return item;
}
}
// The above loop always returns, so we must hint to the
// type checker that it isn't possible to get down here
unreachable!();
}
```
*/
/// A utility macro for indicating unreachable code. It will fail if
/// executed. This is occasionally useful to put after loops that never
/// terminate normally, but instead directly return from a function.
///
/// # Example
///
/// ```rust
/// fn choose_weighted_item(v: &[Item]) -> Item {
/// assert!(!v.is_empty());
/// let mut so_far = 0u;
/// for item in v.iter() {
/// so_far += item.weight;
/// if so_far > 100 {
/// return item;
/// }
/// }
/// // The above loop always returns, so we must hint to the
/// // type checker that it isn't possible to get down here
/// unreachable!();
/// }
/// ```
macro_rules! unreachable (() => (
fail!(\"internal error: entered unreachable code\");
fail!("internal error: entered unreachable code");
))
macro_rules! condition (
@ -968,18 +960,18 @@ pub fn std_macros() -> @str {
)
)
// externfn! declares a wrapper for an external function.
// It is intended to be used like:
//
// externfn!(#[nolink]
// fn memcmp(cx: *u8, ct: *u8, n: u32) -> u32)
//
// Due to limitations in the macro parser, this pattern must be
// implemented with 4 distinct patterns (with attrs / without
// attrs CROSS with args / without ARGS).
//
// Also, this macro grammar allows for any number of return types
// because I couldn't figure out the syntax to specify at most one.
/// externfn! declares a wrapper for an external function.
/// It is intended to be used like:
///
/// externfn!(#[nolink]
/// fn memcmp(cx: *u8, ct: *u8, n: u32) -> u32)
///
/// Due to limitations in the macro parser, this pattern must be
/// implemented with 4 distinct patterns (with attrs / without
/// attrs CROSS with args / without ARGS).
///
/// Also, this macro grammar allows for any number of return types
/// because I couldn't figure out the syntax to specify at most one.
macro_rules! externfn(
(fn $name:ident () $(-> $ret_ty:ty),*) => (
pub unsafe fn $name() $(-> $ret_ty),* {
@ -1045,7 +1037,7 @@ pub fn std_macros() -> @str {
)
)
}";
}"#
}
struct Injector {

View File

@ -1,13 +0,0 @@
// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:failed to find an implementation of trait std::sys::FailWithCause for int
fn main() { fail!(5); }

View File

@ -1,12 +0,0 @@
// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:failed to find an implementation of trait std::sys::FailWithCause for ~[int]
fn main() { fail!(~[0i]); }

View File

@ -1,15 +0,0 @@
// Copyright 2013 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:failed at 'test-fail-send-str'
fn main() {
fail!("test-fail-send-str".into_send_str());
}

View File

@ -25,6 +25,6 @@ pub fn main() {
let _b = Foo;
};
let s = x.unwrap_err().move::<SendStr>().unwrap();
let s = x.unwrap_err().move::<&'static str>().unwrap();
assert_eq!(s.as_slice(), "This failure should happen.");
}