core: Add a limited implementation of failure

This adds an small of failure to libcore, hamstrung by the fact that std::fmt
hasn't been migrated yet. A few asserts were re-worked to not use std::fmt
features, but these asserts can go back to their original form once std::fmt has
migrated.

The current failure implementation is to just have some symbols exposed by
std::rt::unwind that are linked against by libcore. This is an explicit circular
dependency, unfortunately. This will be officially supported in the future
through compiler support with much nicer failure messages. Additionally, there
are two depended-upon symbols today, but in the future there will only be one
(once std::fmt has migrated).
This commit is contained in:
Alex Crichton 2014-05-01 10:47:18 -07:00
parent 4686cf2018
commit e4271cae54
8 changed files with 155 additions and 77 deletions

View File

@ -214,7 +214,7 @@ pub fn is_digit_radix(c: char, radix: uint) -> bool {
#[inline]
pub fn to_digit(c: char, radix: uint) -> Option<uint> {
if radix > 36 {
fail!("to_digit: radix {} is too high (maximum 36)", radix);
fail!("to_digit: radix is too high (maximum 36)");
}
let val = match c {
'0' .. '9' => c as uint - ('0' as uint),
@ -273,7 +273,7 @@ pub fn to_lowercase(c: char) -> char {
#[inline]
pub fn from_digit(num: uint, radix: uint) -> Option<char> {
if radix > 36 {
fail!("from_digit: radix {} is to high (maximum 36)", num);
fail!("from_digit: radix is to high (maximum 36)");
}
if num < radix {
unsafe {

54
src/libcore/failure.rs Normal file
View File

@ -0,0 +1,54 @@
// 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 <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.
//! Failure support for libcore
#![allow(dead_code)]
use str::raw::c_str_to_static_slice;
// FIXME: Once std::fmt is in libcore, all of these functions should delegate
// to a common failure function with this signature:
//
// extern {
// fn rust_unwind(f: &fmt::Arguments, file: &str, line: uint) -> !;
// }
//
// Each of these functions can create a temporary fmt::Arguments
// structure to pass to this function.
#[cold] #[inline(never)] // this is the slow path, always
#[lang="fail_"]
#[cfg(not(test))]
fn fail_(expr: *u8, file: *u8, line: uint) -> ! {
unsafe {
let expr = c_str_to_static_slice(expr as *i8);
let file = c_str_to_static_slice(file as *i8);
begin_unwind(expr, file, line)
}
}
#[cold]
#[lang="fail_bounds_check"]
#[cfg(not(test))]
fn fail_bounds_check(file: *u8, line: uint, index: uint, len: uint) -> ! {
#[allow(ctypes)]
extern { fn rust_fail_bounds_check(file: *u8, line: uint,
index: uint, len: uint,) -> !; }
unsafe { rust_fail_bounds_check(file, line, index, len) }
}
#[cold]
pub fn begin_unwind(msg: &str, file: &'static str, line: uint) -> ! {
#[allow(ctypes)]
extern { fn rust_begin_unwind(msg: &str, file: &'static str,
line: uint) -> !; }
unsafe { rust_begin_unwind(msg, file, line) }
}

View File

@ -21,6 +21,8 @@
#![feature(globs, macro_rules, managed_boxes)]
#![deny(missing_doc)]
mod macros;
#[path = "num/float_macros.rs"] mod float_macros;
#[path = "num/int_macros.rs"] mod int_macros;
#[path = "num/uint_macros.rs"] mod uint_macros;
@ -75,7 +77,11 @@ pub mod slice;
pub mod str;
pub mod tuple;
// FIXME: this module should not exist
mod failure;
// FIXME: this module should not exist. Once owned allocations are no longer a
// language type, this module can move outside to the owned allocation
// crate.
mod should_not_exist;
mod std {

38
src/libcore/macros.rs Normal file
View File

@ -0,0 +1,38 @@
// 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 <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.
#![macro_escape]
/// Entry point of failure, for details, see std::macros
#[macro_export]
macro_rules! fail(
() => (
fail!("explicit failure")
);
($msg:expr) => (
::failure::begin_unwind($msg, file!(), line!())
);
)
/// Runtime assertion, for details see std::macros
#[macro_export]
macro_rules! assert(
($cond:expr) => (
if !$cond {
fail!(concat!("assertion failed: ", stringify!($cond)))
}
);
)
/// Runtime assertion, disableable at compile time
#[macro_export]
macro_rules! debug_assert(
($($arg:tt)*) => (if cfg!(not(ndebug)) { assert!($($arg)*); })
)

View File

@ -255,7 +255,7 @@ mod tests {
fn test_overflows() {
assert!(MAX > 0);
assert!(MIN <= 0);
assert_eq!(MIN + MAX + 1, 0);
assert!(MIN + MAX + 1 == 0);
}
#[test]
@ -265,25 +265,25 @@ mod tests {
#[test]
pub fn test_abs() {
assert_eq!((1 as $T).abs(), 1 as $T);
assert_eq!((0 as $T).abs(), 0 as $T);
assert_eq!((-1 as $T).abs(), 1 as $T);
assert!((1 as $T).abs() == 1 as $T);
assert!((0 as $T).abs() == 0 as $T);
assert!((-1 as $T).abs() == 1 as $T);
}
#[test]
fn test_abs_sub() {
assert_eq!((-1 as $T).abs_sub(&(1 as $T)), 0 as $T);
assert_eq!((1 as $T).abs_sub(&(1 as $T)), 0 as $T);
assert_eq!((1 as $T).abs_sub(&(0 as $T)), 1 as $T);
assert_eq!((1 as $T).abs_sub(&(-1 as $T)), 2 as $T);
assert!((-1 as $T).abs_sub(&(1 as $T)) == 0 as $T);
assert!((1 as $T).abs_sub(&(1 as $T)) == 0 as $T);
assert!((1 as $T).abs_sub(&(0 as $T)) == 1 as $T);
assert!((1 as $T).abs_sub(&(-1 as $T)) == 2 as $T);
}
#[test]
fn test_signum() {
assert_eq!((1 as $T).signum(), 1 as $T);
assert_eq!((0 as $T).signum(), 0 as $T);
assert_eq!((-0 as $T).signum(), 0 as $T);
assert_eq!((-1 as $T).signum(), -1 as $T);
assert!((1 as $T).signum() == 1 as $T);
assert!((0 as $T).signum() == 0 as $T);
assert!((-0 as $T).signum() == 0 as $T);
assert!((-1 as $T).signum() == -1 as $T);
}
#[test]
@ -304,33 +304,33 @@ mod tests {
#[test]
fn test_bitwise() {
assert_eq!(0b1110 as $T, (0b1100 as $T).bitor(&(0b1010 as $T)));
assert_eq!(0b1000 as $T, (0b1100 as $T).bitand(&(0b1010 as $T)));
assert_eq!(0b0110 as $T, (0b1100 as $T).bitxor(&(0b1010 as $T)));
assert_eq!(0b1110 as $T, (0b0111 as $T).shl(&(1 as $T)));
assert_eq!(0b0111 as $T, (0b1110 as $T).shr(&(1 as $T)));
assert_eq!(-(0b11 as $T) - (1 as $T), (0b11 as $T).not());
assert!(0b1110 as $T == (0b1100 as $T).bitor(&(0b1010 as $T)));
assert!(0b1000 as $T == (0b1100 as $T).bitand(&(0b1010 as $T)));
assert!(0b0110 as $T == (0b1100 as $T).bitxor(&(0b1010 as $T)));
assert!(0b1110 as $T == (0b0111 as $T).shl(&(1 as $T)));
assert!(0b0111 as $T == (0b1110 as $T).shr(&(1 as $T)));
assert!(-(0b11 as $T) - (1 as $T) == (0b11 as $T).not());
}
#[test]
fn test_count_ones() {
assert_eq!((0b0101100 as $T).count_ones(), 3);
assert_eq!((0b0100001 as $T).count_ones(), 2);
assert_eq!((0b1111001 as $T).count_ones(), 5);
assert!((0b0101100 as $T).count_ones() == 3);
assert!((0b0100001 as $T).count_ones() == 2);
assert!((0b1111001 as $T).count_ones() == 5);
}
#[test]
fn test_count_zeros() {
assert_eq!((0b0101100 as $T).count_zeros(), BITS as $T - 3);
assert_eq!((0b0100001 as $T).count_zeros(), BITS as $T - 2);
assert_eq!((0b1111001 as $T).count_zeros(), BITS as $T - 5);
assert!((0b0101100 as $T).count_zeros() == BITS as $T - 3);
assert!((0b0100001 as $T).count_zeros() == BITS as $T - 2);
assert!((0b1111001 as $T).count_zeros() == BITS as $T - 5);
}
#[test]
fn test_signed_checked_div() {
assert_eq!(10i.checked_div(&2), Some(5));
assert_eq!(5i.checked_div(&0), None);
assert_eq!(int::MIN.checked_div(&-1), None);
assert!(10i.checked_div(&2) == Some(5));
assert!(5i.checked_div(&0) == None);
assert!(int::MIN.checked_div(&-1) == None);
}
}

View File

@ -192,7 +192,7 @@ mod tests {
fn test_overflows() {
assert!(MAX > 0);
assert!(MIN <= 0);
assert_eq!(MIN + MAX + 1, 0);
assert!(MIN + MAX + 1 == 0);
}
#[test]
@ -202,32 +202,32 @@ mod tests {
#[test]
fn test_bitwise() {
assert_eq!(0b1110 as $T, (0b1100 as $T).bitor(&(0b1010 as $T)));
assert_eq!(0b1000 as $T, (0b1100 as $T).bitand(&(0b1010 as $T)));
assert_eq!(0b0110 as $T, (0b1100 as $T).bitxor(&(0b1010 as $T)));
assert_eq!(0b1110 as $T, (0b0111 as $T).shl(&(1 as $T)));
assert_eq!(0b0111 as $T, (0b1110 as $T).shr(&(1 as $T)));
assert_eq!(MAX - (0b1011 as $T), (0b1011 as $T).not());
assert!(0b1110 as $T == (0b1100 as $T).bitor(&(0b1010 as $T)));
assert!(0b1000 as $T == (0b1100 as $T).bitand(&(0b1010 as $T)));
assert!(0b0110 as $T == (0b1100 as $T).bitxor(&(0b1010 as $T)));
assert!(0b1110 as $T == (0b0111 as $T).shl(&(1 as $T)));
assert!(0b0111 as $T == (0b1110 as $T).shr(&(1 as $T)));
assert!(MAX - (0b1011 as $T) == (0b1011 as $T).not());
}
#[test]
fn test_count_ones() {
assert_eq!((0b0101100 as $T).count_ones(), 3);
assert_eq!((0b0100001 as $T).count_ones(), 2);
assert_eq!((0b1111001 as $T).count_ones(), 5);
assert!((0b0101100 as $T).count_ones() == 3);
assert!((0b0100001 as $T).count_ones() == 2);
assert!((0b1111001 as $T).count_ones() == 5);
}
#[test]
fn test_count_zeros() {
assert_eq!((0b0101100 as $T).count_zeros(), BITS as $T - 3);
assert_eq!((0b0100001 as $T).count_zeros(), BITS as $T - 2);
assert_eq!((0b1111001 as $T).count_zeros(), BITS as $T - 5);
assert!((0b0101100 as $T).count_zeros() == BITS as $T - 3);
assert!((0b0100001 as $T).count_zeros() == BITS as $T - 2);
assert!((0b1111001 as $T).count_zeros() == BITS as $T - 5);
}
#[test]
fn test_unsigned_checked_div() {
assert_eq!(10u.checked_div(&2), Some(5));
assert_eq!(5u.checked_div(&0), None);
assert!(10u.checked_div(&2) == Some(5));
assert!(5u.checked_div(&0) == None);
}
}

View File

@ -70,7 +70,7 @@ use self::task::{Task, BlockedTask};
pub use self::util::default_sched_threads;
// Export unwinding facilities used by the failure macros
pub use self::unwind::{begin_unwind, begin_unwind_raw, begin_unwind_fmt};
pub use self::unwind::{begin_unwind, begin_unwind_fmt};
pub use self::util::{Stdio, Stdout, Stderr};

View File

@ -58,7 +58,6 @@
// Currently Rust uses unwind runtime provided by libgcc.
use any::{Any, AnyRefExt};
use c_str::CString;
use cast;
use fmt;
use kinds::Send;
@ -298,42 +297,23 @@ pub mod eabi {
}
#[cold]
#[lang="fail_"]
#[no_mangle]
#[cfg(not(test))]
pub fn fail_(expr: *u8, file: *u8, line: uint) -> ! {
begin_unwind_raw(expr, file, line);
}
#[cold]
#[lang="fail_bounds_check"]
#[cfg(not(test))]
pub fn fail_bounds_check(file: *u8, line: uint, index: uint, len: uint) -> ! {
use c_str::ToCStr;
pub extern fn rust_fail_bounds_check(file: *u8, line: uint,
index: uint, len: uint) -> ! {
use str::raw::c_str_to_static_slice;
let msg = format!("index out of bounds: the len is {} but the index is {}",
len as uint, index as uint);
msg.with_c_str(|buf| fail_(buf as *u8, file, line))
begin_unwind(msg, unsafe { c_str_to_static_slice(file as *i8) }, line)
}
/// 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.
#[inline(never)] #[cold] // this is the slow path, please never inline this
pub fn begin_unwind_raw(msg: *u8, file: *u8, line: uint) -> ! {
use libc::c_char;
#[inline]
fn static_char_ptr(p: *u8) -> &'static str {
let s = unsafe { CString::new(p as *c_char, false) };
match s.as_str() {
Some(s) => unsafe { cast::transmute::<&str, &'static str>(s) },
None => rtabort!("message wasn't utf8?")
}
}
let msg = static_char_ptr(msg);
let file = static_char_ptr(file);
begin_unwind(msg, file, line as uint)
// Entry point of failure from the libcore crate
#[no_mangle]
#[cfg(not(test))]
pub extern fn rust_begin_unwind(msg: &str, file: &'static str, line: uint) -> ! {
use str::StrAllocating;
begin_unwind(msg.to_owned(), file, line)
}
/// The entry point for unwinding with a formatted message.