mundane/boringssl/
abort.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Macros and functions that abort instead of unwinding.
6//!
7//! Writing `unsafe` code which retains memory safety in the face of unwinding
8//! is [notoriously
9//! difficult](https://doc.rust-lang.org/nightly/nomicon/exception-safety.html).
10//! This module provides panic-related macros and functions that abort rather
11//! than unwind. These are used in place of unwinding-based macros and functions
12//! so that we can avoid the high probability of us getting unwind-safe code
13//! wrong.
14
15use std::fmt::Debug;
16
17macro_rules! assert_abort {
18    ($cond:expr) => ({
19        let cond = $cond;
20        let cond_str = stringify!($cond);
21        assert_abort!(cond, "{}", cond_str);
22    });
23    ($cond:expr,) => ({
24        assert_abort!($cond);
25    });
26    ($cond:expr, $msg:expr, $($arg:tt)*) => ({
27        if !($cond) {
28            panic_abort!(concat!("assertion failed: ", $msg), $($arg)*);
29        }
30    });
31}
32
33macro_rules! assert_abort_eq {
34    ($left:expr, $right:expr) => ({
35        match (&$left, &$right) {
36            (left_val, right_val) => {
37                if !(*left_val == *right_val) {
38                    panic_abort!(r#"assertion failed: `(left == right)`
39  left: `{:?}`,
40 right: `{:?}`"#, left_val, right_val)
41                }
42            }
43        }
44    });
45    ($left:expr, $right:expr,) => ({
46        assert_eq!($left, $right)
47    });
48    ($left:expr, $right:expr, $($arg:tt)+) => ({
49        match (&($left), &($right)) {
50            (left_val, right_val) => {
51                if !(*left_val == *right_val) {
52                    panic_abort!(r#"assertion failed: `(left == right)`
53  left: `{:?}`,
54 right: `{:?}`: {}"#, left_val, right_val,
55                           format_args!($($arg)+))
56                }
57            }
58        }
59    });
60}
61
62#[allow(unused)]
63macro_rules! unimplemented_abort {
64    () => {{
65        panic_abort!("not yet implemented")
66    }};
67}
68
69macro_rules! unreachable_abort {
70    () => {{
71        panic_abort!("internal error: entered unreachable code")
72    }};
73}
74
75macro_rules! panic_abort {
76    () => ({
77        panic_abort!("explicit panic")
78    });
79    ($msg:expr) => ({
80        eprintln!("{}", $msg);
81        ::std::process::abort();
82    });
83    ($msg:expr,) => ({
84        panic_abort!($msg)
85    });
86    ($fmt:expr, $($arg:tt)+) => ({
87        panic_abort!(format!($fmt, $($arg)+));
88    });
89}
90
91// Redefine normal panic/assert macros so their use will cause a compiler error.
92
93#[allow(unused)]
94macro_rules! panic {
95    ($($x:tt)*) => {
96        compile_error!("use panic_abort! instead of panic! in boringssl module")
97    };
98}
99
100#[allow(unused)]
101macro_rules! assert {
102    ($($x:tt)*) => {
103        compile_error!("use assert_abort! instead of assert! in boringssl module")
104    };
105}
106
107#[allow(unused)]
108macro_rules! assert_eq {
109    ($($x:tt)*) => {
110        compile_error!("use assert_abort_eq! instead of assert_eq! in boringssl module")
111    };
112}
113
114#[allow(unused)]
115macro_rules! assert_ne {
116    ($($x:tt)*) => {
117        compile_error!("use assert_abort_ne! instead of assert_ne! in boringssl module")
118    };
119}
120
121#[allow(unused)]
122macro_rules! unimplemented {
123    ($($x:tt)*) => {
124        compile_error!("use unimplemented_abort! instead of unimplemented! in boringssl module")
125    };
126}
127
128#[allow(unused)]
129macro_rules! unreachable {
130    ($($x:tt)*) => {
131        compile_error!("use unreachable_abort! instead of unreachable! in boringssl module")
132    };
133}
134
135// unwrap and expect
136
137// TODO(joshlf): Is there a way (maybe with clippy) that we can cause warnings
138// or errors if this module ever uses unwrap or expect?
139
140pub trait UnwrapAbort {
141    type Item;
142
143    fn unwrap_abort(self) -> Self::Item;
144    fn expect_abort(self, msg: &str) -> Self::Item;
145}
146
147// The implementations for Option and Result are adapted from the Rust standard library.
148impl<T> UnwrapAbort for Option<T> {
149    type Item = T;
150
151    fn unwrap_abort(self) -> T {
152        match self {
153            Some(val) => val,
154            None => panic_abort!("called `Option::unwrap_abort()` on a `None` value"),
155        }
156    }
157
158    fn expect_abort(self, msg: &str) -> T {
159        // This is a separate function to reduce the code size of alloc_expect itself
160        #[inline(never)]
161        #[cold]
162        fn failed(msg: &str) -> ! {
163            panic_abort!("{}", msg);
164        }
165
166        match self {
167            Some(val) => val,
168            None => failed(msg),
169        }
170    }
171}
172
173impl<T, E: Debug> UnwrapAbort for Result<T, E> {
174    type Item = T;
175
176    fn unwrap_abort(self) -> T {
177        match self {
178            Ok(val) => val,
179            Err(err) => {
180                result_unwrap_failed("called `Result::unwrap_abort()` on an `Err` value", err)
181            }
182        }
183    }
184
185    fn expect_abort(self, msg: &str) -> T {
186        match self {
187            Ok(val) => val,
188            Err(err) => result_unwrap_failed(msg, err),
189        }
190    }
191}
192
193// This is a separate function to reduce the code size of alloc_{expect,unwrap}
194#[inline(never)]
195#[cold]
196fn result_unwrap_failed<E: Debug>(msg: &str, err: E) -> ! {
197    panic_abort!("{}: {:?}", msg, err)
198}