auto merge of #17894 : steveklabnik/rust/fail_to_panic, r=aturon
This in-progress PR implements https://github.com/rust-lang/rust/issues/17489. I made the code changes in this commit, next is to go through alllllllll the documentation and fix various things. - Rename column headings as appropriate, `# Panics` for panic conditions and `# Errors` for `Result`s. - clean up usage of words like 'fail' in error messages Anything else to add to the list, @aturon ? I think I should leave the actual functions with names like `slice_or_fail` alone, since you'll get to those in your conventions work? I'm submitting just the code bits now so that we can see it separately, and I also don't want to have to keep re-building rust over and over again if I don't have to 😉 Listing all the bits so I can remember as I go: - [x] compiler-rt - [x] compiletest - [x] doc - [x] driver - [x] etc - [x] grammar - [x] jemalloc - [x] liballoc - [x] libarena - [x] libbacktrace - [x] libcollections - [x] libcore - [x] libcoretest - [x] libdebug - [x] libflate - [x] libfmt_macros - [x] libfourcc - [x] libgetopts - [x] libglob - [x] libgraphviz - [x] libgreen - [x] libhexfloat - [x] liblibc - [x] liblog - [x] libnative - [x] libnum - [x] librand - [x] librbml - [x] libregex - [x] libregex_macros - [x] librlibc - [x] librustc - [x] librustc_back - [x] librustc_llvm - [x] librustdoc - [x] librustrt - [x] libsemver - [x] libserialize - [x] libstd - [x] libsync - [x] libsyntax - [x] libterm - [x] libtest - [x] libtime - [x] libunicode - [x] liburl - [x] libuuid - [x] llvm - [x] rt - [x] test
This commit is contained in:
commit
77f44d4a7b
518 changed files with 1776 additions and 1727 deletions
|
|
@ -94,9 +94,9 @@ code should need to run is a stack.
|
|||
|
||||
`match` being exhaustive has some useful properties. First, if every
|
||||
possibility is covered by the `match`, adding further variants to the `enum`
|
||||
in the future will prompt a compilation failure, rather than runtime failure.
|
||||
in the future will prompt a compilation failure, rather than runtime panic.
|
||||
Second, it makes cost explicit. In general, only safe way to have a
|
||||
non-exhaustive match would be to fail the task if nothing is matched, though
|
||||
non-exhaustive match would be to panic the task if nothing is matched, though
|
||||
it could fall through if the type of the `match` expression is `()`. This sort
|
||||
of hidden cost and special casing is against the language's philosophy. It's
|
||||
easy to ignore certain cases by using the `_` wildcard:
|
||||
|
|
|
|||
|
|
@ -65,14 +65,15 @@ Data values in the language can only be constructed through a fixed set of initi
|
|||
* There is no global inter-crate namespace; all name management occurs within a crate.
|
||||
* Using another crate binds the root of _its_ namespace into the user's namespace.
|
||||
|
||||
## Why is failure unwinding non-recoverable within a task? Why not try to "catch exceptions"?
|
||||
## Why is panic unwinding non-recoverable within a task? Why not try to "catch exceptions"?
|
||||
|
||||
In short, because too few guarantees could be made about the dynamic environment of the catch block, as well as invariants holding in the unwound heap, to be able to safely resume; we believe that other methods of signalling and logging errors are more appropriate, with tasks playing the role of a "hard" isolation boundary between separate heaps.
|
||||
|
||||
Rust provides, instead, three predictable and well-defined options for handling any combination of the three main categories of "catch" logic:
|
||||
|
||||
* Failure _logging_ is done by the integrated logging subsystem.
|
||||
* _Recovery_ after a failure is done by trapping a task failure from _outside_ the task, where other tasks are known to be unaffected.
|
||||
* _Recovery_ after a panic is done by trapping a task panic from _outside_
|
||||
the task, where other tasks are known to be unaffected.
|
||||
* _Cleanup_ of resources is done by RAII-style objects with destructors.
|
||||
|
||||
Cleanup through RAII-style destructors is more likely to work than in catch blocks anyways, since it will be better tested (part of the non-error control paths, so executed all the time).
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ the stack of the task which is spawned.
|
|||
|
||||
Foreign libraries often hand off ownership of resources to the calling code.
|
||||
When this occurs, we must use Rust's destructors to provide safety and guarantee
|
||||
the release of these resources (especially in the case of failure).
|
||||
the release of these resources (especially in the case of panic).
|
||||
|
||||
# Callbacks from C code to Rust functions
|
||||
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ match x {
|
|||
// complicated stuff goes here
|
||||
return result + val;
|
||||
},
|
||||
_ => fail!("Didn't get good_2")
|
||||
_ => panic!("Didn't get good_2")
|
||||
}
|
||||
}
|
||||
_ => return 0 // default value
|
||||
|
|
@ -284,7 +284,7 @@ macro_rules! biased_match (
|
|||
biased_match!((x) ~ (Good1(g1, val)) else { return 0 };
|
||||
binds g1, val )
|
||||
biased_match!((g1.body) ~ (Good2(result) )
|
||||
else { fail!("Didn't get good_2") };
|
||||
else { panic!("Didn't get good_2") };
|
||||
binds result )
|
||||
// complicated stuff goes here
|
||||
return result + val;
|
||||
|
|
@ -397,7 +397,7 @@ macro_rules! biased_match (
|
|||
# fn f(x: T1) -> uint {
|
||||
biased_match!(
|
||||
(x) ~ (Good1(g1, val)) else { return 0 };
|
||||
(g1.body) ~ (Good2(result) ) else { fail!("Didn't get Good2") };
|
||||
(g1.body) ~ (Good2(result) ) else { panic!("Didn't get Good2") };
|
||||
binds val, result )
|
||||
// complicated stuff goes here
|
||||
return result + val;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ relates to the Rust type system, and introduce the fundamental library
|
|||
abstractions for constructing concurrent programs.
|
||||
|
||||
Tasks provide failure isolation and recovery. When a fatal error occurs in Rust
|
||||
code as a result of an explicit call to `fail!()`, an assertion failure, or
|
||||
code as a result of an explicit call to `panic!()`, an assertion failure, or
|
||||
another invalid operation, the runtime system destroys the entire task. Unlike
|
||||
in languages such as Java and C++, there is no way to `catch` an exception.
|
||||
Instead, tasks may monitor each other for failure.
|
||||
Instead, tasks may monitor each other to see if they panic.
|
||||
|
||||
Tasks use Rust's type system to provide strong memory safety guarantees. In
|
||||
particular, the type system guarantees that tasks cannot induce a data race
|
||||
|
|
@ -317,19 +317,19 @@ spawn(proc() {
|
|||
# }
|
||||
```
|
||||
|
||||
# Handling task failure
|
||||
# Handling task panics
|
||||
|
||||
Rust has a built-in mechanism for raising exceptions. The `fail!()` macro
|
||||
(which can also be written with an error string as an argument: `fail!(
|
||||
~reason)`) and the `assert!` construct (which effectively calls `fail!()` if a
|
||||
Rust has a built-in mechanism for raising exceptions. The `panic!()` macro
|
||||
(which can also be written with an error string as an argument: `panic!(
|
||||
~reason)`) and the `assert!` construct (which effectively calls `panic!()` if a
|
||||
boolean expression is false) are both ways to raise exceptions. When a task
|
||||
raises an exception, the task unwinds its stack—running destructors and
|
||||
freeing memory along the way—and then exits. Unlike exceptions in C++,
|
||||
exceptions in Rust are unrecoverable within a single task: once a task fails,
|
||||
exceptions in Rust are unrecoverable within a single task: once a task panics,
|
||||
there is no way to "catch" the exception.
|
||||
|
||||
While it isn't possible for a task to recover from failure, tasks may notify
|
||||
each other of failure. The simplest way of handling task failure is with the
|
||||
While it isn't possible for a task to recover from panicking, tasks may notify
|
||||
each other if they panic. The simplest way of handling a panic is with the
|
||||
`try` function, which is similar to `spawn`, but immediately blocks and waits
|
||||
for the child task to finish. `try` returns a value of type
|
||||
`Result<T, Box<Any + Send>>`. `Result` is an `enum` type with two variants:
|
||||
|
|
@ -346,7 +346,7 @@ let result: Result<int, Box<std::any::Any + Send>> = task::try(proc() {
|
|||
if some_condition() {
|
||||
calculate_result()
|
||||
} else {
|
||||
fail!("oops!");
|
||||
panic!("oops!");
|
||||
}
|
||||
});
|
||||
assert!(result.is_err());
|
||||
|
|
@ -355,18 +355,18 @@ assert!(result.is_err());
|
|||
Unlike `spawn`, the function spawned using `try` may return a value, which
|
||||
`try` will dutifully propagate back to the caller in a [`Result`] enum. If the
|
||||
child task terminates successfully, `try` will return an `Ok` result; if the
|
||||
child task fails, `try` will return an `Error` result.
|
||||
child task panics, `try` will return an `Error` result.
|
||||
|
||||
[`Result`]: std/result/index.html
|
||||
|
||||
> *Note:* A failed task does not currently produce a useful error
|
||||
> *Note:* A panicked task does not currently produce a useful error
|
||||
> value (`try` always returns `Err(())`). In the
|
||||
> future, it may be possible for tasks to intercept the value passed to
|
||||
> `fail!()`.
|
||||
> `panic!()`.
|
||||
|
||||
But not all failures are created equal. In some cases you might need to abort
|
||||
But not all panics are created equal. In some cases you might need to abort
|
||||
the entire program (perhaps you're writing an assert which, if it trips,
|
||||
indicates an unrecoverable logic error); in other cases you might want to
|
||||
contain the failure at a certain boundary (perhaps a small piece of input from
|
||||
contain the panic at a certain boundary (perhaps a small piece of input from
|
||||
the outside world, which you happen to be processing in parallel, is malformed
|
||||
such that the processing task cannot proceed).
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ value. To run the tests in a crate, it must be compiled with the
|
|||
`--test` flag: `rustc myprogram.rs --test -o myprogram-tests`. Running
|
||||
the resulting executable will run all the tests in the crate. A test
|
||||
is considered successful if its function returns; if the task running
|
||||
the test fails, through a call to `fail!`, a failed `assert`, or some
|
||||
the test fails, through a call to `panic!`, a failed `assert`, or some
|
||||
other (`assert_eq`, ...) means, then the test fails.
|
||||
|
||||
When compiling a crate with the `--test` flag `--cfg test` is also
|
||||
|
|
@ -77,7 +77,7 @@ test on windows you can write `#[cfg_attr(windows, ignore)]`.
|
|||
|
||||
Tests that are intended to fail can be annotated with the
|
||||
`should_fail` attribute. The test will be run, and if it causes its
|
||||
task to fail then the test will be counted as successful; otherwise it
|
||||
task to panic then the test will be counted as successful; otherwise it
|
||||
will be counted as a failure. For example:
|
||||
|
||||
~~~test_harness
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ code:
|
|||
- implement the `Drop` for resource clean-up via a destructor, and use
|
||||
RAII (Resource Acquisition Is Initialization). This reduces the need
|
||||
for any manual memory management by users, and automatically ensures
|
||||
that clean-up is always run, even when the task fails.
|
||||
that clean-up is always run, even when the task panics.
|
||||
- ensure that any data stored behind a raw pointer is destroyed at the
|
||||
appropriate time.
|
||||
|
||||
|
|
@ -462,7 +462,7 @@ fn start(_argc: int, _argv: *const *const u8) -> int {
|
|||
// provided by libstd.
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "fail_fmt"] fn fail_fmt() -> ! { loop {} }
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
# // fn main() {} tricked you, rustdoc!
|
||||
```
|
||||
|
||||
|
|
@ -485,7 +485,7 @@ pub extern fn main(argc: int, argv: *const *const u8) -> int {
|
|||
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "fail_fmt"] fn fail_fmt() -> ! { loop {} }
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
# // fn main() {} tricked you, rustdoc!
|
||||
```
|
||||
|
||||
|
|
@ -504,8 +504,8 @@ The second of these three functions, `eh_personality`, is used by the
|
|||
failure mechanisms of the compiler. This is often mapped to GCC's
|
||||
personality function (see the
|
||||
[libstd implementation](std/rt/unwind/index.html) for more
|
||||
information), but crates which do not trigger failure can be assured
|
||||
that this function is never called. The final function, `fail_fmt`, is
|
||||
information), but crates which do not trigger a panic can be assured
|
||||
that this function is never called. The final function, `panic_fmt`, is
|
||||
also used by the failure mechanisms of the compiler.
|
||||
|
||||
## Using libcore
|
||||
|
|
@ -565,8 +565,8 @@ pub extern fn dot_product(a: *const u32, a_len: u32,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#[lang = "fail_fmt"]
|
||||
extern fn fail_fmt(args: &core::fmt::Arguments,
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn panic_fmt(args: &core::fmt::Arguments,
|
||||
file: &str,
|
||||
line: uint) -> ! {
|
||||
loop {}
|
||||
|
|
@ -579,9 +579,9 @@ extern fn fail_fmt(args: &core::fmt::Arguments,
|
|||
```
|
||||
|
||||
Note that there is one extra lang item here which differs from the examples
|
||||
above, `fail_fmt`. This must be defined by consumers of libcore because the
|
||||
core library declares failure, but it does not define it. The `fail_fmt`
|
||||
lang item is this crate's definition of failure, and it must be guaranteed to
|
||||
above, `panic_fmt`. This must be defined by consumers of libcore because the
|
||||
core library declares panics, but it does not define it. The `panic_fmt`
|
||||
lang item is this crate's definition of panic, and it must be guaranteed to
|
||||
never return.
|
||||
|
||||
As can be seen in this example, the core library is intended to provide the
|
||||
|
|
@ -686,7 +686,7 @@ fn main(argc: int, argv: *const *const u8) -> int {
|
|||
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "fail_fmt"] fn fail_fmt() -> ! { loop {} }
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
```
|
||||
|
||||
Note the use of `abort`: the `exchange_malloc` lang item is assumed to
|
||||
|
|
|
|||
|
|
@ -5213,17 +5213,17 @@ immediately.
|
|||
|
||||
## Success and failure
|
||||
|
||||
Tasks don't always succeed, they can also fail. A task that wishes to fail
|
||||
can call the `fail!` macro, passing a message:
|
||||
Tasks don't always succeed, they can also panic. A task that wishes to panic
|
||||
can call the `panic!` macro, passing a message:
|
||||
|
||||
```{rust}
|
||||
spawn(proc() {
|
||||
fail!("Nope.");
|
||||
panic!("Nope.");
|
||||
});
|
||||
```
|
||||
|
||||
If a task fails, it is not possible for it to recover. However, it can
|
||||
notify other tasks that it has failed. We can do this with `task::try`:
|
||||
If a task panics, it is not possible for it to recover. However, it can
|
||||
notify other tasks that it has panicked. We can do this with `task::try`:
|
||||
|
||||
```{rust}
|
||||
use std::task;
|
||||
|
|
@ -5233,14 +5233,14 @@ let result = task::try(proc() {
|
|||
if rand::random() {
|
||||
println!("OK");
|
||||
} else {
|
||||
fail!("oops!");
|
||||
panic!("oops!");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This task will randomly fail or succeed. `task::try` returns a `Result`
|
||||
This task will randomly panic or succeed. `task::try` returns a `Result`
|
||||
type, so we can handle the response like any other computation that may
|
||||
fail.
|
||||
panic.
|
||||
|
||||
# Macros
|
||||
|
||||
|
|
|
|||
|
|
@ -818,15 +818,15 @@ mod math {
|
|||
type Complex = (f64, f64);
|
||||
fn sin(f: f64) -> f64 {
|
||||
/* ... */
|
||||
# fail!();
|
||||
# panic!();
|
||||
}
|
||||
fn cos(f: f64) -> f64 {
|
||||
/* ... */
|
||||
# fail!();
|
||||
# panic!();
|
||||
}
|
||||
fn tan(f: f64) -> f64 {
|
||||
/* ... */
|
||||
# fail!();
|
||||
# panic!();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1195,12 +1195,12 @@ output slot type would normally be. For example:
|
|||
```
|
||||
fn my_err(s: &str) -> ! {
|
||||
println!("{}", s);
|
||||
fail!();
|
||||
panic!();
|
||||
}
|
||||
```
|
||||
|
||||
We call such functions "diverging" because they never return a value to the
|
||||
caller. Every control path in a diverging function must end with a `fail!()` or
|
||||
caller. Every control path in a diverging function must end with a `panic!()` or
|
||||
a call to another diverging function on every control path. The `!` annotation
|
||||
does *not* denote a type. Rather, the result type of a diverging function is a
|
||||
special type called $\bot$ ("bottom") that unifies with any type. Rust has no
|
||||
|
|
@ -1213,7 +1213,7 @@ were declared without the `!` annotation, the following code would not
|
|||
typecheck:
|
||||
|
||||
```
|
||||
# fn my_err(s: &str) -> ! { fail!() }
|
||||
# fn my_err(s: &str) -> ! { panic!() }
|
||||
|
||||
fn f(i: int) -> int {
|
||||
if i == 42 {
|
||||
|
|
@ -2260,7 +2260,7 @@ These types help drive the compiler's analysis
|
|||
: Allocate memory on the exchange heap.
|
||||
* `closure_exchange_malloc`
|
||||
: ___Needs filling in___
|
||||
* `fail_`
|
||||
* `panic`
|
||||
: Abort the program with an error.
|
||||
* `fail_bounds_check`
|
||||
: Abort the program with a bounds check error.
|
||||
|
|
@ -2867,11 +2867,11 @@ be assigned to.
|
|||
|
||||
Indices are zero-based, and may be of any integral type. Vector access is
|
||||
bounds-checked at run-time. When the check fails, it will put the task in a
|
||||
_failing state_.
|
||||
_panicked state_.
|
||||
|
||||
```{should-fail}
|
||||
([1, 2, 3, 4])[0];
|
||||
(["a", "b"])[10]; // fails
|
||||
(["a", "b"])[10]; // panics
|
||||
```
|
||||
|
||||
### Unary operator expressions
|
||||
|
|
@ -3301,9 +3301,9 @@ enum List<X> { Nil, Cons(X, Box<List<X>>) }
|
|||
let x: List<int> = Cons(10, box Cons(11, box Nil));
|
||||
|
||||
match x {
|
||||
Cons(_, box Nil) => fail!("singleton list"),
|
||||
Cons(_, box Nil) => panic!("singleton list"),
|
||||
Cons(..) => return,
|
||||
Nil => fail!("empty list")
|
||||
Nil => panic!("empty list")
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -3374,7 +3374,7 @@ match x {
|
|||
return;
|
||||
}
|
||||
_ => {
|
||||
fail!();
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -3396,7 +3396,7 @@ fn is_sorted(list: &List) -> bool {
|
|||
Cons(x, ref r @ box Cons(_, _)) => {
|
||||
match *r {
|
||||
box Cons(y, _) => (x <= y) && is_sorted(&**r),
|
||||
_ => fail!()
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3460,7 +3460,7 @@ may refer to the variables bound within the pattern they follow.
|
|||
let message = match maybe_digit {
|
||||
Some(x) if x < 10 => process_digit(x),
|
||||
Some(x) => process_other(x),
|
||||
None => fail!()
|
||||
None => panic!()
|
||||
};
|
||||
```
|
||||
|
||||
|
|
@ -4092,7 +4092,7 @@ cause transitions between the states. The lifecycle states of a task are:
|
|||
|
||||
* running
|
||||
* blocked
|
||||
* failing
|
||||
* panicked
|
||||
* dead
|
||||
|
||||
A task begins its lifecycle — once it has been spawned — in the
|
||||
|
|
@ -4104,21 +4104,21 @@ it makes a blocking communication call. When the call can be completed —
|
|||
when a message arrives at a sender, or a buffer opens to receive a message
|
||||
— then the blocked task will unblock and transition back to *running*.
|
||||
|
||||
A task may transition to the *failing* state at any time, due being killed by
|
||||
some external event or internally, from the evaluation of a `fail!()` macro.
|
||||
Once *failing*, a task unwinds its stack and transitions to the *dead* state.
|
||||
A task may transition to the *panicked* state at any time, due being killed by
|
||||
some external event or internally, from the evaluation of a `panic!()` macro.
|
||||
Once *panicking*, a task unwinds its stack and transitions to the *dead* state.
|
||||
Unwinding the stack of a task is done by the task itself, on its own control
|
||||
stack. If a value with a destructor is freed during unwinding, the code for the
|
||||
destructor is run, also on the task's control stack. Running the destructor
|
||||
code causes a temporary transition to a *running* state, and allows the
|
||||
destructor code to cause any subsequent state transitions. The original task
|
||||
of unwinding and failing thereby may suspend temporarily, and may involve
|
||||
of unwinding and panicking thereby may suspend temporarily, and may involve
|
||||
(recursive) unwinding of the stack of a failed destructor. Nonetheless, the
|
||||
outermost unwinding activity will continue until the stack is unwound and the
|
||||
task transitions to the *dead* state. There is no way to "recover" from task
|
||||
failure. Once a task has temporarily suspended its unwinding in the *failing*
|
||||
state, failure occurring from within this destructor results in *hard* failure.
|
||||
A hard failure currently results in the process aborting.
|
||||
panics. Once a task has temporarily suspended its unwinding in the *panicking*
|
||||
state, a panic occurring from within this destructor results in *hard* panic.
|
||||
A hard panic currently results in the process aborting.
|
||||
|
||||
A task in the *dead* state cannot transition to other states; it exists only to
|
||||
have its termination status inspected by other tasks, and/or to await
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ directive.
|
|||
|
||||
~~~md
|
||||
```should_fail
|
||||
// This code block is expected to generate a failure when run
|
||||
// This code block is expected to generate a panic when run
|
||||
```
|
||||
~~~
|
||||
|
||||
|
|
@ -189,7 +189,7 @@ were passed to the compiler using the `test_harness` directive.
|
|||
```test_harness
|
||||
#[test]
|
||||
fn foo() {
|
||||
fail!("oops! (will run & register as failure)")
|
||||
panic!("oops! (will run & register as a failed test)")
|
||||
}
|
||||
```
|
||||
~~~
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue