diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-02-04 19:02:10 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2014-02-06 15:48:56 -0800 |
| commit | 454882dcb7fdb03867d695a88335e2d2c8f7561a (patch) | |
| tree | 467d0884fb80c987d234d2762f392c64e19d7570 /src/libstd | |
| parent | f039d10cf73d8bc9a57fcdfe38c5f0b0a3a98b5a (diff) | |
| download | rust-454882dcb7fdb03867d695a88335e2d2c8f7561a.tar.gz rust-454882dcb7fdb03867d695a88335e2d2c8f7561a.zip | |
Remove std::condition
This has been a long time coming. Conditions in rust were initially envisioned
as being a good alternative to error code return pattern. The idea is that all
errors are fatal-by-default, and you can opt-in to handling the error by
registering an error handler.
While sounding nice, conditions ended up having some unforseen shortcomings:
* Actually handling an error has some very awkward syntax:
let mut result = None;
let mut answer = None;
io::io_error::cond.trap(|e| { result = Some(e) }).inside(|| {
answer = Some(some_io_operation());
});
match result {
Some(err) => { /* hit an I/O error */ }
None => {
let answer = answer.unwrap();
/* deal with the result of I/O */
}
}
This pattern can certainly use functions like io::result, but at its core
actually handling conditions is fairly difficult
* The "zero value" of a function is often confusing. One of the main ideas
behind using conditions was to change the signature of I/O functions. Instead
of read_be_u32() returning a result, it returned a u32. Errors were notified
via a condition, and if you caught the condition you understood that the "zero
value" returned is actually a garbage value. These zero values are often
difficult to understand, however.
One case of this is the read_bytes() function. The function takes an integer
length of the amount of bytes to read, and returns an array of that size. The
array may actually be shorter, however, if an error occurred.
Another case is fs::stat(). The theoretical "zero value" is a blank stat
struct, but it's a little awkward to create and return a zero'd out stat
struct on a call to stat().
In general, the return value of functions that can raise error are much more
natural when using a Result as opposed to an always-usable zero-value.
* Conditions impose a necessary runtime requirement on *all* I/O. In theory I/O
is as simple as calling read() and write(), but using conditions imposed the
restriction that a rust local task was required if you wanted to catch errors
with I/O. While certainly an surmountable difficulty, this was always a bit of
a thorn in the side of conditions.
* Functions raising conditions are not always clear that they are raising
conditions. This suffers a similar problem to exceptions where you don't
actually know whether a function raises a condition or not. The documentation
likely explains, but if someone retroactively adds a condition to a function
there's nothing forcing upstream users to acknowledge a new point of task
failure.
* Libaries using I/O are not guaranteed to correctly raise on conditions when an
error occurs. In developing various I/O libraries, it's much easier to just
return `None` from a read rather than raising an error. The silent contract of
"don't raise on EOF" was a little difficult to understand and threw a wrench
into the answer of the question "when do I raise a condition?"
Many of these difficulties can be overcome through documentation, examples, and
general practice. In the end, all of these difficulties added together ended up
being too overwhelming and improving various aspects didn't end up helping that
much.
A result-based I/O error handling strategy also has shortcomings, but the
cognitive burden is much smaller. The tooling necessary to make this strategy as
usable as conditions were is much smaller than the tooling necessary for
conditions.
Perhaps conditions may manifest themselves as a future entity, but for now
we're going to remove them from the standard library.
Closes #9795
Closes #8968
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/c_str.rs | 50 | ||||
| -rw-r--r-- | src/libstd/condition.rs | 349 | ||||
| -rw-r--r-- | src/libstd/fmt/parse.rs | 65 | ||||
| -rw-r--r-- | src/libstd/lib.rs | 3 | ||||
| -rw-r--r-- | src/libstd/macros.rs | 44 | ||||
| -rw-r--r-- | src/libstd/path/mod.rs | 85 | ||||
| -rw-r--r-- | src/libstd/path/posix.rs | 86 | ||||
| -rw-r--r-- | src/libstd/path/windows.rs | 88 | ||||
| -rw-r--r-- | src/libstd/rt/mod.rs | 1 |
9 files changed, 78 insertions, 693 deletions
diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 7e79ad97df0..6fc31b673ac 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -75,24 +75,11 @@ use ptr::RawPtr; use ptr; use str::StrSlice; use str; -use vec::{CloneableVector, ImmutableVector, MutableVector}; +use vec::{ImmutableVector, MutableVector}; use vec; use unstable::intrinsics; use rt::global_heap::malloc_raw; -/// Resolution options for the `null_byte` condition -pub enum NullByteResolution { - /// Truncate at the null byte - Truncate, - /// Use a replacement byte - ReplaceWith(libc::c_char) -} - -condition! { - // This should be &[u8] but there's a lifetime issue (#5370). - pub null_byte: (~[u8]) -> NullByteResolution; -} - /// The representation of a C String. /// /// This structure wraps a `*libc::c_char`, and will automatically free the @@ -252,7 +239,7 @@ pub trait ToCStr { /// /// # Failure /// - /// Raises the `null_byte` condition if the receiver has an interior null. + /// Fails the task if the receiver has an interior null. fn to_c_str(&self) -> CString; /// Unsafe variant of `to_c_str()` that doesn't check for nulls. @@ -273,7 +260,7 @@ pub trait ToCStr { /// /// # Failure /// - /// Raises the `null_byte` condition if the receiver has an interior null. + /// Fails the task if the receiver has an interior null. #[inline] fn with_c_str<T>(&self, f: |*libc::c_char| -> T) -> T { self.to_c_str().with_ref(f) @@ -362,12 +349,7 @@ fn check_for_null(v: &[u8], buf: *mut libc::c_char) { for i in range(0, v.len()) { unsafe { let p = buf.offset(i as int); - if *p == 0 { - match null_byte::cond.raise(v.to_owned()) { - Truncate => break, - ReplaceWith(c) => *p = c - } - } + assert!(*p != 0); } } } @@ -541,29 +523,9 @@ mod tests { #[test] fn test_to_c_str_fail() { - use c_str::null_byte::cond; - + use task; let mut error_happened = false; - cond.trap(|err| { - assert_eq!(err, bytes!("he", 0, "llo").to_owned()) - error_happened = true; - Truncate - }).inside(|| "he\x00llo".to_c_str()); - assert!(error_happened); - - cond.trap(|_| { - ReplaceWith('?' as libc::c_char) - }).inside(|| "he\x00llo".to_c_str()).with_ref(|buf| { - unsafe { - assert_eq!(*buf.offset(0), 'h' as libc::c_char); - assert_eq!(*buf.offset(1), 'e' as libc::c_char); - assert_eq!(*buf.offset(2), '?' as libc::c_char); - assert_eq!(*buf.offset(3), 'l' as libc::c_char); - assert_eq!(*buf.offset(4), 'l' as libc::c_char); - assert_eq!(*buf.offset(5), 'o' as libc::c_char); - assert_eq!(*buf.offset(6), 0); - } - }) + assert!(task::try(proc() { "he\x00llo".to_c_str() }).is_err()); } #[test] diff --git a/src/libstd/condition.rs b/src/libstd/condition.rs deleted file mode 100644 index e0dc5c8b65d..00000000000 --- a/src/libstd/condition.rs +++ /dev/null @@ -1,349 +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. - -/*! - -Condition handling - -Conditions are a utility used to deal with handling error conditions. The syntax -of a condition handler strikes a resemblance to try/catch blocks in other -languages, but condition handlers are *not* a form of exception handling in the -same manner. - -A condition is declared through the `condition!` macro provided by the compiler: - -```rust -condition! { - pub my_error: int -> ~str; -} -# fn main() {} -``` - -This macro declares an inner module called `my_error` with one static variable, -`cond` that is a static `Condition` instance. To help understand what the other -parameters are used for, an example usage of this condition would be: - -```rust -# condition! { pub my_error: int -> ~str; } -# fn main() { - -my_error::cond.trap(|raised_int| { - - // the condition `my_error` was raised on, and the value it raised is stored - // in `raised_int`. This closure must return a `~str` type (as specified in - // the declaration of the condition - if raised_int == 3 { ~"three" } else { ~"oh well" } - -}).inside(|| { - - // The condition handler above is installed for the duration of this block. - // That handler will override any previous handler, but the previous handler - // is restored when this block returns (handlers nest) - // - // If any code from this block (or code from another block) raises on the - // condition, then the above handler will be invoked (so long as there's no - // other nested handler). - - println!("{}", my_error::cond.raise(3)); // prints "three" - println!("{}", my_error::cond.raise(4)); // prints "oh well" - -}) - -# } - ``` - -Condition handling is useful in cases where propagating errors is either to -cumbersome or just not necessary in the first place. It should also be noted, -though, that if there is not handler installed when a condition is raised, then -the task invokes `fail!()` and will terminate. - -## More Info - -Condition handlers as an error strategy is well explained in the [conditions -tutorial](http://static.rust-lang.org/doc/master/tutorial-conditions.html), -along with comparing and contrasting it with other error handling strategies. - -*/ - -use local_data; -use prelude::*; -use unstable::raw::Closure; - -#[doc(hidden)] -pub struct Handler<T, U> { - priv handle: Closure, - priv prev: Option<@Handler<T, U>>, -} - -/// This struct represents the state of a condition handler. It contains a key -/// into TLS which holds the currently install handler, along with the name of -/// the condition (useful for debugging). -/// -/// This struct should never be created directly, but rather only through the -/// `condition!` macro provided to all libraries using `std`. -pub struct Condition<T, U> { - /// Name of the condition handler - name: &'static str, - /// TLS key used to insert/remove values in TLS. - key: local_data::Key<@Handler<T, U>> -} - -impl<T, U> Condition<T, U> { - /// Creates an object which binds the specified handler. This will also save - /// the current handler *on creation* such that when the `Trap` is consumed, - /// it knows which handler to restore. - /// - /// # Example - /// - /// ```rust - /// condition! { my_error: int -> int; } - /// - /// # fn main() { - /// let trap = my_error::cond.trap(|error| error + 3); - /// - /// // use `trap`'s inside method to register the handler and then run a - /// // block of code with the handler registered - /// # } - /// ``` - pub fn trap<'a>(&'a self, h: 'a |T| -> U) -> Trap<'a, T, U> { - let h: Closure = unsafe { ::cast::transmute(h) }; - let prev = local_data::get(self.key, |k| k.map(|x| *x)); - let h = @Handler { handle: h, prev: prev }; - Trap { cond: self, handler: h } - } - - /// Raises on this condition, invoking any handler if one has been - /// registered, or failing the current task otherwise. - /// - /// While a condition handler is being run, the condition will have no - /// handler listed, so a task failure will occur if the condition is - /// re-raised during the handler. - /// - /// # Arguments - /// - /// * t - The argument to pass along to the condition handler. - /// - /// # Return value - /// - /// If a handler is found, its return value is returned, otherwise this - /// function will not return. - pub fn raise(&self, t: T) -> U { - let msg = format!("Unhandled condition: {}: {:?}", self.name, t); - self.raise_default(t, || fail!("{}", msg.clone())) - } - - /// Performs the same functionality as `raise`, except that when no handler - /// is found the `default` argument is called instead of failing the task. - pub fn raise_default(&self, t: T, default: || -> U) -> U { - match local_data::pop(self.key) { - None => { - debug!("Condition.raise: found no handler"); - default() - } - Some(handler) => { - debug!("Condition.raise: found handler"); - match handler.prev { - None => {} - Some(hp) => local_data::set(self.key, hp) - } - let handle : |T| -> U = unsafe { - ::cast::transmute(handler.handle) - }; - let u = handle(t); - local_data::set(self.key, handler); - u - } - } - } -} - -/// A `Trap` is created when the `trap` method is invoked on a `Condition`, and -/// it is used to actually bind a handler into the TLS slot reserved for this -/// condition. -/// -/// Normally this object is not dealt with directly, but rather it's directly -/// used after being returned from `trap` -pub struct Trap<'a, T, U> { - priv cond: &'a Condition<T, U>, - priv handler: @Handler<T, U> -} - -impl<'a, T, U> Trap<'a, T, U> { - /// Execute a block of code with this trap handler's exception handler - /// registered. - /// - /// # Example - /// - /// ```rust - /// condition! { my_error: int -> int; } - /// - /// # fn main() { - /// let result = my_error::cond.trap(|error| error + 3).inside(|| { - /// my_error::cond.raise(4) - /// }); - /// assert_eq!(result, 7); - /// # } - /// ``` - pub fn inside<V>(&self, inner: 'a || -> V) -> V { - let _g = Guard { cond: self.cond }; - debug!("Trap: pushing handler to TLS"); - local_data::set(self.cond.key, self.handler); - inner() - } - - /// Returns a guard that will automatically reset the condition upon - /// exit of the scope. This is useful if you want to use conditions with - /// an RAII pattern. - pub fn guard(&self) -> Guard<'a,T,U> { - let guard = Guard { - cond: self.cond - }; - debug!("Guard: pushing handler to TLS"); - local_data::set(self.cond.key, self.handler); - guard - } -} - -/// A guard that will automatically reset the condition handler upon exit of -/// the scope. This is useful if you want to use conditions with an RAII -/// pattern. -pub struct Guard<'a, T, U> { - priv cond: &'a Condition<T, U> -} - -#[unsafe_destructor] -impl<'a, T, U> Drop for Guard<'a, T, U> { - fn drop(&mut self) { - debug!("Guard: popping handler from TLS"); - let curr = local_data::pop(self.cond.key); - match curr { - None => {} - Some(h) => match h.prev { - None => {} - Some(hp) => local_data::set(self.cond.key, hp) - } - } - } -} - -#[cfg(test)] -mod test { - condition! { - sadness: int -> int; - } - - fn trouble(i: int) { - debug!("trouble: raising condition"); - let j = sadness::cond.raise(i); - debug!("trouble: handler recovered with {}", j); - } - - fn nested_trap_test_inner() { - let mut inner_trapped = false; - - sadness::cond.trap(|_j| { - debug!("nested_trap_test_inner: in handler"); - inner_trapped = true; - 0 - }).inside(|| { - debug!("nested_trap_test_inner: in protected block"); - trouble(1); - }); - - assert!(inner_trapped); - } - - #[test] - fn nested_trap_test_outer() { - let mut outer_trapped = false; - - sadness::cond.trap(|_j| { - debug!("nested_trap_test_outer: in handler"); - outer_trapped = true; 0 - }).inside(|| { - debug!("nested_guard_test_outer: in protected block"); - nested_trap_test_inner(); - trouble(1); - }); - - assert!(outer_trapped); - } - - fn nested_reraise_trap_test_inner() { - let mut inner_trapped = false; - - sadness::cond.trap(|_j| { - debug!("nested_reraise_trap_test_inner: in handler"); - inner_trapped = true; - let i = 10; - debug!("nested_reraise_trap_test_inner: handler re-raising"); - sadness::cond.raise(i) - }).inside(|| { - debug!("nested_reraise_trap_test_inner: in protected block"); - trouble(1); - }); - - assert!(inner_trapped); - } - - #[test] - fn nested_reraise_trap_test_outer() { - let mut outer_trapped = false; - - sadness::cond.trap(|_j| { - debug!("nested_reraise_trap_test_outer: in handler"); - outer_trapped = true; 0 - }).inside(|| { - debug!("nested_reraise_trap_test_outer: in protected block"); - nested_reraise_trap_test_inner(); - }); - - assert!(outer_trapped); - } - - #[test] - fn test_default() { - let mut trapped = false; - - sadness::cond.trap(|j| { - debug!("test_default: in handler"); - sadness::cond.raise_default(j, || { trapped=true; 5 }) - }).inside(|| { - debug!("test_default: in protected block"); - trouble(1); - }); - - assert!(trapped); - } - - // Issue #6009 - mod m { - condition! { - // #6009, #8215: should this truly need a `pub` for access from n? - pub sadness: int -> int; - } - - mod n { - use super::sadness; - - #[test] - fn test_conditions_are_public() { - let mut trapped = false; - sadness::cond.trap(|_| { - trapped = true; - 0 - }).inside(|| { - sadness::cond.raise(0); - }); - assert!(trapped); - } - } - } -} diff --git a/src/libstd/fmt/parse.rs b/src/libstd/fmt/parse.rs index 6c08eae7474..555fe29bed0 100644 --- a/src/libstd/fmt/parse.rs +++ b/src/libstd/fmt/parse.rs @@ -19,8 +19,6 @@ use prelude::*; use char; use str; -condition! { pub parse_error: ~str -> (); } - /// A piece is a portion of the format string which represents the next part to /// emit. These are emitted as a stream by the `Parser` class. #[deriving(Eq)] @@ -170,6 +168,8 @@ pub struct Parser<'a> { priv input: &'a str, priv cur: str::CharOffsets<'a>, priv depth: uint, + /// Error messages accumulated during parsing + errors: ~[~str], } impl<'a> Iterator<Piece<'a>> for Parser<'a> { @@ -207,14 +207,15 @@ impl<'a> Parser<'a> { input: s, cur: s.char_indices(), depth: 0, + errors: ~[], } } /// Notifies of an error. The message doesn't actually need to be of type /// ~str, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err(&self, msg: &str) { - parse_error::cond.raise("invalid format string: " + msg); + fn err(&mut self, msg: &str) { + self.errors.push(msg.to_owned()); } /// Optionally consumes the specified character. If the character is not at @@ -671,7 +672,9 @@ mod tests { } fn musterr(s: &str) { - Parser::new(s).next(); + let mut p = Parser::new(s); + p.next(); + assert!(p.errors.len() != 0); } #[test] @@ -684,12 +687,12 @@ mod tests { same("\\}", ~[String("}")]); } - #[test] #[should_fail] fn invalid01() { musterr("{") } - #[test] #[should_fail] fn invalid02() { musterr("\\") } - #[test] #[should_fail] fn invalid03() { musterr("\\a") } - #[test] #[should_fail] fn invalid04() { musterr("{3a}") } - #[test] #[should_fail] fn invalid05() { musterr("{:|}") } - #[test] #[should_fail] fn invalid06() { musterr("{:>>>}") } + #[test] fn invalid01() { musterr("{") } + #[test] fn invalid02() { musterr("\\") } + #[test] fn invalid03() { musterr("\\a") } + #[test] fn invalid04() { musterr("{3a}") } + #[test] fn invalid05() { musterr("{:|}") } + #[test] fn invalid06() { musterr("{:>>>}") } #[test] fn format_nothing() { @@ -916,36 +919,16 @@ mod tests { })]); } - #[test] #[should_fail] fn badselect01() { - musterr("{select, }") - } - #[test] #[should_fail] fn badselect02() { - musterr("{1, select}") - } - #[test] #[should_fail] fn badselect03() { - musterr("{1, select, }") - } - #[test] #[should_fail] fn badselect04() { - musterr("{1, select, a {}}") - } - #[test] #[should_fail] fn badselect05() { - musterr("{1, select, other }}") - } - #[test] #[should_fail] fn badselect06() { - musterr("{1, select, other {}") - } - #[test] #[should_fail] fn badselect07() { - musterr("{select, other {}") - } - #[test] #[should_fail] fn badselect08() { - musterr("{1 select, other {}") - } - #[test] #[should_fail] fn badselect09() { - musterr("{:d select, other {}") - } - #[test] #[should_fail] fn badselect10() { - musterr("{1:d select, other {}") - } + #[test] fn badselect01() { musterr("{select, }") } + #[test] fn badselect02() { musterr("{1, select}") } + #[test] fn badselect03() { musterr("{1, select, }") } + #[test] fn badselect04() { musterr("{1, select, a {}}") } + #[test] fn badselect05() { musterr("{1, select, other }}") } + #[test] fn badselect06() { musterr("{1, select, other {}") } + #[test] fn badselect07() { musterr("{select, other {}") } + #[test] fn badselect08() { musterr("{1 select, other {}") } + #[test] fn badselect09() { musterr("{:d select, other {}") } + #[test] fn badselect10() { musterr("{1:d select, other {}") } #[test] fn plural_simple() { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index adce11fed2d..0398af9c1c1 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -178,8 +178,6 @@ pub mod run; pub mod cast; pub mod fmt; pub mod cleanup; -#[deprecated] -pub mod condition; pub mod logging; pub mod util; pub mod mem; @@ -216,7 +214,6 @@ mod std { pub use clone; pub use cmp; pub use comm; - pub use condition; pub use fmt; pub use io; pub use kinds; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index b31ae92d742..cd81d6037e6 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -124,50 +124,6 @@ macro_rules! unreachable (() => ( )) #[macro_export] -macro_rules! condition ( - - { pub $c:ident: $input:ty -> $out:ty; } => { - - pub mod $c { - #[allow(unused_imports)]; - #[allow(non_uppercase_statics)]; - #[allow(missing_doc)]; - - use super::*; - - local_data_key!(key: @::std::condition::Handler<$input, $out>) - - pub static cond : - ::std::condition::Condition<$input,$out> = - ::std::condition::Condition { - name: stringify!($c), - key: key - }; - } - }; - - { $c:ident: $input:ty -> $out:ty; } => { - - mod $c { - #[allow(unused_imports)]; - #[allow(non_uppercase_statics)]; - #[allow(dead_code)]; - - use super::*; - - local_data_key!(key: @::std::condition::Handler<$input, $out>) - - pub static cond : - ::std::condition::Condition<$input,$out> = - ::std::condition::Condition { - name: stringify!($c), - key: key - }; - } - } -) - -#[macro_export] macro_rules! format(($($arg:tt)*) => ( format_args!(::std::fmt::format, $($arg)*) )) diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index 4aa4a3feab1..f3f70c263ec 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -147,12 +147,6 @@ pub use is_sep_byte = self::windows::is_sep_byte; pub mod posix; pub mod windows; -// Condition that is raised when a NUL is found in a byte vector given to a Path function -condition! { - // this should be a &[u8] but there's a lifetime issue - null_byte: ~[u8] -> ~[u8]; -} - /// A trait that represents the generic operations available on paths pub trait GenericPath: Clone + GenericPathUnsafe { /// Creates a new Path from a byte vector or string. @@ -160,18 +154,13 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// # Failure /// - /// Raises the `null_byte` condition if the path contains a NUL. + /// Fails the task if the path contains a NUL. /// /// See individual Path impls for additional restrictions. #[inline] fn new<T: BytesContainer>(path: T) -> Self { - if contains_nul(path.container_as_bytes()) { - let path = self::null_byte::cond.raise(path.container_into_owned_bytes()); - assert!(!contains_nul(path)); - unsafe { GenericPathUnsafe::new_unchecked(path) } - } else { - unsafe { GenericPathUnsafe::new_unchecked(path) } - } + assert!(!contains_nul(path.container_as_bytes())); + unsafe { GenericPathUnsafe::new_unchecked(path) } } /// Creates a new Path from a byte vector or string, if possible. @@ -283,16 +272,11 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// # Failure /// - /// Raises the `null_byte` condition if the filename contains a NUL. + /// Fails the task if the filename contains a NUL. #[inline] fn set_filename<T: BytesContainer>(&mut self, filename: T) { - if contains_nul(filename.container_as_bytes()) { - let filename = self::null_byte::cond.raise(filename.container_into_owned_bytes()); - assert!(!contains_nul(filename)); - unsafe { self.set_filename_unchecked(filename) } - } else { - unsafe { self.set_filename_unchecked(filename) } - } + assert!(!contains_nul(filename.container_as_bytes())); + unsafe { self.set_filename_unchecked(filename) } } /// Replaces the extension with the given byte vector or string. /// If there is no extension in `self`, this adds one. @@ -301,8 +285,9 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// # Failure /// - /// Raises the `null_byte` condition if the extension contains a NUL. + /// Fails the task if the extension contains a NUL. fn set_extension<T: BytesContainer>(&mut self, extension: T) { + assert!(!contains_nul(extension.container_as_bytes())); // borrowck causes problems here too let val = { match self.filename() { @@ -315,21 +300,11 @@ pub trait GenericPath: Clone + GenericPathUnsafe { None } else { let mut v; - if contains_nul(extension.container_as_bytes()) { - let ext = extension.container_into_owned_bytes(); - let extension = self::null_byte::cond.raise(ext); - assert!(!contains_nul(extension)); - v = vec::with_capacity(name.len() + extension.len() + 1); - v.push_all(name); - v.push(dot); - v.push_all(extension); - } else { - let extension = extension.container_as_bytes(); - v = vec::with_capacity(name.len() + extension.len() + 1); - v.push_all(name); - v.push(dot); - v.push_all(extension); - } + let extension = extension.container_as_bytes(); + v = vec::with_capacity(name.len() + extension.len() + 1); + v.push_all(name); + v.push(dot); + v.push_all(extension); Some(v) } } @@ -338,19 +313,10 @@ pub trait GenericPath: Clone + GenericPathUnsafe { Some(name.slice_to(idx).to_owned()) } else { let mut v; - if contains_nul(extension.container_as_bytes()) { - let ext = extension.container_into_owned_bytes(); - let extension = self::null_byte::cond.raise(ext); - assert!(!contains_nul(extension)); - v = vec::with_capacity(idx + extension.len() + 1); - v.push_all(name.slice_to(idx+1)); - v.push_all(extension); - } else { - let extension = extension.container_as_bytes(); - v = vec::with_capacity(idx + extension.len() + 1); - v.push_all(name.slice_to(idx+1)); - v.push_all(extension); - } + let extension = extension.container_as_bytes(); + v = vec::with_capacity(idx + extension.len() + 1); + v.push_all(name.slice_to(idx+1)); + v.push_all(extension); Some(v) } } @@ -370,7 +336,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// # Failure /// - /// Raises the `null_byte` condition if the filename contains a NUL. + /// Fails the task if the filename contains a NUL. #[inline] fn with_filename<T: BytesContainer>(&self, filename: T) -> Self { let mut p = self.clone(); @@ -383,7 +349,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// # Failure /// - /// Raises the `null_byte` condition if the extension contains a NUL. + /// Fails the task if the extension contains a NUL. #[inline] fn with_extension<T: BytesContainer>(&self, extension: T) -> Self { let mut p = self.clone(); @@ -408,16 +374,11 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// # Failure /// - /// Raises the `null_byte` condition if the path contains a NUL. + /// Fails the task if the path contains a NUL. #[inline] fn push<T: BytesContainer>(&mut self, path: T) { - if contains_nul(path.container_as_bytes()) { - let path = self::null_byte::cond.raise(path.container_into_owned_bytes()); - assert!(!contains_nul(path)); - unsafe { self.push_unchecked(path) } - } else { - unsafe { self.push_unchecked(path) } - } + assert!(!contains_nul(path.container_as_bytes())); + unsafe { self.push_unchecked(path) } } /// Pushes multiple paths (as byte vectors or strings) onto `self`. /// See `push` for details. @@ -445,7 +406,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// # Failure /// - /// Raises the `null_byte` condition if the path contains a NUL. + /// Fails the task if the path contains a NUL. #[inline] fn join<T: BytesContainer>(&self, path: T) -> Self { let mut p = self.clone(); diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index ba0cd0bb521..6970ebfb15d 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -318,7 +318,7 @@ impl Path { /// /// # Failure /// - /// Raises the `null_byte` condition if the vector contains a NUL. + /// Fails the task if the vector contains a NUL. #[inline] pub fn new<T: BytesContainer>(path: T) -> Path { GenericPath::new(path) @@ -527,83 +527,21 @@ mod tests { #[test] fn test_null_byte() { - use path::null_byte::cond; - - let mut handled = false; - let mut p = cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("foo/bar", 0)); - (b!("/bar").to_owned()) - }).inside(|| { + use task; + let result = task::try(proc() { Path::new(b!("foo/bar", 0)) }); - assert!(handled); - assert_eq!(p.as_vec(), b!("/bar")); - - handled = false; - cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("f", 0, "o")); - (b!("foo").to_owned()) - }).inside(|| { - p.set_filename(b!("f", 0, "o")) - }); - assert!(handled); - assert_eq!(p.as_vec(), b!("/foo")); - - handled = false; - cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("f", 0, "o")); - (b!("foo").to_owned()) - }).inside(|| { - p.push(b!("f", 0, "o")); - }); - assert!(handled); - assert_eq!(p.as_vec(), b!("/foo/foo")); - } - - #[test] - fn test_null_byte_fail() { - use path::null_byte::cond; - use task; + assert!(result.is_err()); - macro_rules! t( - ($name:expr => $code:expr) => ( - { - let mut t = task::task(); - t.name($name); - let res = t.try(proc() $code); - assert!(res.is_err()); - } - ) - ) + let result = task::try(proc() { + Path::new("test").set_filename(b!("f", 0, "o")) + }); + assert!(result.is_err()); - t!(~"new() w/nul" => { - cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside(|| { - Path::new(b!("foo/bar", 0)) - }); - }) - - t!(~"set_filename w/nul" => { - let mut p = Path::new(b!("foo/bar")); - cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside(|| { - p.set_filename(b!("foo", 0)) - }); - }) - - t!(~"push w/nul" => { - let mut p = Path::new(b!("foo/bar")); - cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside(|| { - p.push(b!("foo", 0)) - }); - }) + let result = task::try(proc() { + Path::new("test").push(b!("f", 0, "o")); + }); + assert!(result.is_err()); } #[test] diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index eec6f37b627..90154adb7fe 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -590,7 +590,7 @@ impl Path { /// /// # Failure /// - /// Raises the `null_byte` condition if the vector contains a NUL. + /// Fails the task if the vector contains a NUL. /// Fails if invalid UTF-8. #[inline] pub fn new<T: BytesContainer>(path: T) -> Path { @@ -1248,83 +1248,21 @@ mod tests { #[test] fn test_null_byte() { - use path::null_byte::cond; - - let mut handled = false; - let mut p = cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("foo\\bar", 0)); - (b!("\\bar").to_owned()) - }).inside(|| { - Path::new(b!("foo\\bar", 0)) - }); - assert!(handled); - assert_eq!(p.as_vec(), b!("\\bar")); - - handled = false; - cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("f", 0, "o")); - (b!("foo").to_owned()) - }).inside(|| { - p.set_filename(b!("f", 0, "o")) - }); - assert!(handled); - assert_eq!(p.as_vec(), b!("\\foo")); - - handled = false; - cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("f", 0, "o")); - (b!("foo").to_owned()) - }).inside(|| { - p.push(b!("f", 0, "o")); - }); - assert!(handled); - assert_eq!(p.as_vec(), b!("\\foo\\foo")); - } - - #[test] - fn test_null_byte_fail() { - use path::null_byte::cond; use task; + let result = task::try(proc() { + Path::new(b!("foo/bar", 0)) + }); + assert!(result.is_err()); - macro_rules! t( - ($name:expr => $code:expr) => ( - { - let mut t = task::task(); - t.name($name); - let res = t.try(proc() $code); - assert!(res.is_err()); - } - ) - ) - - t!(~"from_vec() w\\nul" => { - cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside(|| { - Path::new(b!("foo\\bar", 0)) - }); - }) - - t!(~"set_filename w\\nul" => { - let mut p = Path::new(b!("foo\\bar")); - cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside(|| { - p.set_filename(b!("foo", 0)) - }); - }) + let result = task::try(proc() { + Path::new("test").set_filename(b!("f", 0, "o")) + }); + assert!(result.is_err()); - t!(~"push w\\nul" => { - let mut p = Path::new(b!("foo\\bar")); - cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside(|| { - p.push(b!("foo", 0)) - }); - }) + let result = task::try(proc() { + Path::new("test").push(b!("f", 0, "o")); + }); + assert!(result.is_err()); } #[test] diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 55425eb2e72..376d685c8ac 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -44,7 +44,6 @@ Several modules in `core` are clients of `rt`: * `std::local_data` - The interface to local data. * `std::gc` - The garbage collector. * `std::unstable::lang` - Miscellaneous lang items, some of which rely on `std::rt`. -* `std::condition` - Uses local data. * `std::cleanup` - Local heap destruction. * `std::io` - In the future `std::io` will use an `rt` implementation. * `std::logging` |
