Add some documentation for how it works?
Closed this issue · 1 comments
i.e. how does it know whether functions called by a no_panic function panic?
Hi. I wanna try to explain, but since I am not an author of the crate, I might be mistaken somehow (also sorry for my English). Hope that'll help a bit.
In README mentioned crate dont_panic
, and current crate is based on it. Lets look there. That crate provides:
dont_panic!
macro;call(f: F)
function (which is based on that macro);dp_assert!
macro (not interesting).
dont_panic!
First we gonna figure out how dont_panic!
works. If we look on it's code, we'll see that it simply calls function. But (and that is explained in comment) this function is extern
, and doesn't exist in any other object file (this is "decided" by function name, you still can export such function and I assume macro won't work). So, idea here: if compiler optimizes out dont_panic!
invocation, there is no call to rust_panic_called_where_shouldnt
, linker won't attempt to find that function, and program compiles. Otherwise, call to rust_panic_called_where_shouldnt
stays, and you get a linking error.
NOTE: macro name might be slightly confusing - it does not detect panic, it just detect whether it is optimized out or not; see example.
NOTE 2: because debug builds does not perform optimizations, this macro doesn't work correctly in debug builds - code is never optimized out!
call(f: F)
Okay, now lets figure out call(f: F)
. It's source:
pub fn call<T, F: FnOnce() -> T>(f: F) -> T {
struct DontPanic;
impl Drop for DontPanic {
fn drop(&mut self) {
dont_panic!();
}
}
let guard = DontPanic;
let result = f();
core::mem::forget(guard);
result
}
What is happening here:
-
Define
DontPanic
andimpl Drop for DontPanic
(withdont_panic!
inside); -
Create instance of type
DontPanic
(guard
); -
Call provided closure
f
; -
If compiler can prove that closure
f
panic-free:
3.1 Execution proceeds toforget(guard)
;
3.2forget
doesn't invokeDrop
, sodrop(&mut self)
(anddont_panic!
inside) is optimized out and program compiles; -
If compiler failed to prove that closure panic-free:
4.1 Unwinding begins;
4.2 During unwindingguard
is getting dropped, and it'sdrop(&mut self)
method is called;
4.3 Since call todrop(&mut self)
is not optimized out,dont_panic!
is still here and we get a linking error.
Back to no-panic
Oookay, back to current crate. Now it should be easy. Link to no_panic
macro. This macro is just parses input (which is function) and wraps its body with something like call(f: F)
(it's pretty similar, right?). Pretty error message is provided with #[link_name = "..."]
"hack".
Thats it!