├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── slice ├── Cargo.toml ├── README.md └── src │ └── lib.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dont_panic" 3 | version = "0.1.0" 4 | authors = ["Martin Habovštiak "] 5 | license = "MITNFA" 6 | description = "panic!()-like macro that causes linking error instead of panicking. May be used to statically ensure some code won't panic." 7 | homepage = "https://github.com/Kixunil/dont_panic" 8 | repository = "https://github.com/Kixunil/dont_panic" 9 | readme = "README.md" 10 | keywords = ["panic", "static-check", "static_assert", "static-assert"] 11 | categories = ["no-std", "rust-patterns"] 12 | 13 | [features] 14 | # Enables panicking 15 | panic = [] 16 | default = [] 17 | 18 | [profile.test] 19 | opt-level = 1 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT +no-false-attribs License 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | Distributions of all or part of the Software intended to be used 16 | by the recipients as they would use the unmodified Software, 17 | containing modifications that substantially alter, remove, or 18 | disable functionality of the Software, outside of the documented 19 | configuration mechanisms provided by the Software, shall be 20 | modified such that the Original Author's bug reporting email 21 | addresses and urls are either replaced with the contact information 22 | of the parties responsible for the changes, or removed entirely. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 26 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 28 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 29 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 30 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 31 | OTHER DEALINGS IN THE SOFTWARE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Don't panic!() 2 | ============== 3 | 4 | Ensures that code can't panic at compile time. 5 | 6 | Example 7 | ------- 8 | 9 | This code will compile and (not) run just fine: 10 | 11 | ```rust 12 | let should_panic = false; 13 | if should_panic { 14 | dont_panic!("This will never execute."); 15 | } 16 | ``` 17 | 18 | However, this code will cause a linking error: 19 | 20 | ```rust 21 | let should_panic = true; 22 | if should_panic { 23 | dont_panic!("This will never execute."); 24 | } 25 | ``` 26 | 27 | Caveats 28 | ------- 29 | 30 | * This works only when the appropriate opt_level is specified - it may require release build. 31 | * The error message is a weird link error. You don't get line number, etc. 32 | * There may be situations in which you know that the code is unreachable but the compiler can't prove it. 33 | -------------------------------------------------------------------------------- /slice/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dont_panic_slice" 3 | version = "0.1.0" 4 | authors = ["Martin Habovštiak "] 5 | license = "MITNFA" 6 | description = "Slice that causes link error instead of panicking." 7 | homepage = "https://github.com/Kixunil/dont_panic" 8 | repository = "https://github.com/Kixunil/dont_panic" 9 | readme = "README.md" 10 | keywords = ["panic", "static-check", "static_assert", "static-assert", "slice"] 11 | categories = ["no-std", "rust-patterns"] 12 | 13 | [features] 14 | default = [] 15 | panic = ["dont_panic/panic"] 16 | 17 | [dependencies] 18 | dont_panic = "0.1" 19 | 20 | [profile.test] 21 | opt-level = 3 22 | -------------------------------------------------------------------------------- /slice/README.md: -------------------------------------------------------------------------------- 1 | Don't panic!() slice 2 | ==================== 3 | 4 | This crate uses `dont_panic` crate to create drop-in replacement for slices. (Not fully drop-in 5 | yet.) The goal is to ensure the code won't ever panic. The user of the crate must prove to the 6 | compiler that the panicking code is unreachable by checking bounds before indexing into slice. 7 | -------------------------------------------------------------------------------- /slice/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Non-panicking drop-in replacement for slices. Instead of panic it causes link time error if 2 | //! bounds are not checked. (Not fully drop-in replacement yet. Some features are missing.) 3 | //! 4 | //! # Example 5 | //! 6 | //! ```no_compile 7 | //! #[macro_use] 8 | //! extern crate dont_panic_slice; 9 | //! 10 | //! use dont_panic_slice::DPSlice; 11 | //! 12 | //! fn main() { 13 | //! let arr = [0, 1, 2, 3]; 14 | //! let dps = <&DPSlice<_>>::from(&arr as &[_]); 15 | //! assert_eq!(dps[0], 0); 16 | //! assert_eq!(dps[3], 3); 17 | //! // This would not compile (instead of run time panicking) 18 | //! assert_eq!(dps[42], 42); 19 | //! } 20 | //! ``` 21 | //! 22 | //! You must compile it with `--release`. If you don't want to slow down debug builds, you can use 23 | //! `--features=panic` to switch to normal panicking behaviour. 24 | 25 | #![no_std] 26 | 27 | #[macro_use] 28 | extern crate dont_panic; 29 | 30 | pub struct DPSlice([T]); 31 | 32 | impl DPSlice { 33 | pub fn as_rust_slice(this: &Self) -> &[T] { 34 | unsafe { ::core::mem::transmute(this) } 35 | } 36 | 37 | pub fn as_rust_slice_mut(this: &mut Self) -> &mut [T] { 38 | unsafe { ::core::mem::transmute(this) } 39 | } 40 | 41 | pub fn len(&self) -> usize { 42 | Self::as_rust_slice(self).len() 43 | } 44 | 45 | pub fn is_empty(&self) -> bool { 46 | Self::as_rust_slice(self).is_empty() 47 | } 48 | 49 | pub fn first(&self) -> Option<&T> { 50 | Self::as_rust_slice(self).first() 51 | } 52 | 53 | pub fn first_mut(&mut self) -> Option<&mut T> { 54 | Self::as_rust_slice_mut(self).first_mut() 55 | } 56 | 57 | pub fn split_first(&self) -> Option<(&T, &[T])> { 58 | Self::as_rust_slice(self).split_first() 59 | } 60 | 61 | pub fn split_first_mut(&self) -> Option<(&T, &[T])> { 62 | Self::as_rust_slice(self).split_first() 63 | } 64 | 65 | pub fn split_last(&self) -> Option<(&T, &[T])> { 66 | Self::as_rust_slice(self).split_last() 67 | } 68 | 69 | pub fn split_last_mut(&mut self) -> Option<(&T, &[T])> { 70 | Self::as_rust_slice_mut(self).split_last() 71 | } 72 | 73 | pub fn swap(&mut self, a: usize, b: usize) { 74 | if a > self.len() { 75 | dont_panic!("index out of bounds: the len is {} but the index is {}", self.len(), a); 76 | } 77 | 78 | if b > self.len() { 79 | dont_panic!("index out of bounds: the len is {} but the index is {}", self.len(), b); 80 | } 81 | 82 | Self::as_rust_slice_mut(self).swap(a, b); 83 | } 84 | 85 | pub fn windows(&self, size: usize) -> ::core::slice::Windows { 86 | dp_assert!(size != 0); 87 | 88 | Self::as_rust_slice(self).windows(size) 89 | } 90 | 91 | pub fn chunks(&self, size: usize) -> ::core::slice::Chunks { 92 | dp_assert!(size != 0); 93 | 94 | Self::as_rust_slice(self).chunks(size) 95 | } 96 | 97 | pub fn chunks_mut(&mut self, size: usize) -> ::core::slice::ChunksMut { 98 | dp_assert!(size != 0); 99 | 100 | Self::as_rust_slice_mut(self).chunks_mut(size) 101 | } 102 | 103 | pub fn split_at(&self, mid: usize) -> (&[T], &[T]) { 104 | if mid > self.len() { 105 | dont_panic!("index {} out of range for slice of length {}", mid, self.len()); 106 | } 107 | 108 | Self::as_rust_slice(self).split_at(mid) 109 | } 110 | 111 | pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { 112 | if mid > self.len() { 113 | dont_panic!("index {} out of range for slice of length {}", mid, self.len()); 114 | } 115 | 116 | Self::as_rust_slice_mut(self).split_at_mut(mid) 117 | } 118 | } 119 | 120 | impl<'a, T> From<&'a [T]> for &'a DPSlice { 121 | fn from(slice: &[T]) -> Self { 122 | unsafe { ::core::mem::transmute(slice) } 123 | } 124 | } 125 | 126 | /* Coherence :'( 127 | impl<'a, T> From<&'a DPSlice> for &'a [T] { 128 | fn from(slice: &DPSlice) -> Self { 129 | unsafe { ::core::mem::transmute(slice) } 130 | } 131 | } 132 | */ 133 | 134 | impl<'a, T> From<&'a mut [T]> for &'a mut DPSlice { 135 | fn from(slice: &mut [T]) -> Self { 136 | unsafe { ::core::mem::transmute(slice) } 137 | } 138 | } 139 | 140 | /* Coherence :'( 141 | impl<'a, T> From<&'a mut DPSlice> for &'a mut [T] { 142 | fn from(slice: &mut DPSlice) -> Self { 143 | unsafe { ::core::mem::transmute(slice) } 144 | } 145 | } 146 | */ 147 | 148 | impl ::core::ops::Index for DPSlice { 149 | type Output = T; 150 | 151 | #[inline(always)] 152 | fn index(&self, index: usize) -> &Self::Output { 153 | Self::as_rust_slice(self).get(index).unwrap_or_else(|| dont_panic!("index out of bounds: the len is {} but the index is {}", self.len(), index)) 154 | } 155 | } 156 | 157 | impl ::core::ops::IndexMut for DPSlice { 158 | #[inline(always)] 159 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 160 | Self::as_rust_slice_mut(self).get_mut(index).unwrap_or_else(|| dont_panic!("index out of bounds: the len is {} but the index is {}", self.len(), index)) 161 | } 162 | } 163 | 164 | #[cfg(test)] 165 | mod tests { 166 | use ::DPSlice; 167 | 168 | #[test] 169 | fn it_works() { 170 | let arr = [0, 1, 2, 3]; 171 | let dps = <&DPSlice<_>>::from(&arr as &[_]); 172 | assert_eq!(dps[0], 0); 173 | assert_eq!(dps[3], 3); 174 | } 175 | 176 | #[cfg(feature = "panic")] 177 | #[test] 178 | #[should_panic] 179 | fn panic() { 180 | let arr = [0, 1, 2, 3]; 181 | let dps = <&DPSlice<_>>::from(&arr as &[_]); 182 | assert_eq!(dps[42], 0); 183 | assert_eq!(dps[3], 3); 184 | } 185 | 186 | #[cfg(feature = "panic")] 187 | #[test] 188 | fn no_panic() { 189 | let arr = [0, 1, 2, 3]; 190 | let dps = <&DPSlice<_>>::from(&arr as &[_]); 191 | assert_eq!(dps[0], 0); 192 | assert_eq!(dps[3], 3); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides macros that look just like `panic!()` but instead of panicking, they cause a 2 | //! linking error if their calls are not optimized-out. This can be used to ensure the compiler 3 | //! optimizes away some code. 4 | //! 5 | //! # Example 6 | //! 7 | //! ```no_compile 8 | //! #[macro_use] 9 | //! extern crate dont_panic; 10 | //! 11 | //! fn main() { 12 | //! /* 13 | //! let x = 6 * 9; 14 | //! if x == 42 { 15 | //! dont_panic!("6 * 9 == 42"); 16 | //! } 17 | //! */ 18 | //! let x = false; 19 | //! if x { 20 | //! dont_panic!("42"); 21 | //! } 22 | //! } 23 | //! ``` 24 | //! 25 | //! Compile with `--release` or `--features=panic` 26 | 27 | #![no_std] 28 | 29 | extern "C" { 30 | /// This function doesn't actually exist. It ensures a linking error if it isn't optimized-out. 31 | pub fn rust_panic_called_where_shouldnt() -> !; 32 | } 33 | 34 | /// This macro doesn't panic. Instead it tries to call a non-existing function. If the compiler can 35 | /// prove it can't be called and optimizes it away, the code will compile just fine. Otherwise you get 36 | /// a linking error. 37 | /// 38 | /// This should be used only in cases you are absolutely sure are OK and optimizable by compiler. 39 | #[cfg(not(feature = "panic"))] 40 | #[macro_export] 41 | macro_rules! dont_panic { 42 | ($($x:tt)*) => ({ 43 | unsafe { $crate::rust_panic_called_where_shouldnt(); } 44 | }) 45 | } 46 | 47 | /// This macro is active only with `panic` feature turned on and it will really panic, instead of 48 | /// causing a linking error. The purpose is to make development easier. (E.g. in debug mode.) 49 | #[cfg(feature = "panic")] 50 | #[macro_export] 51 | macro_rules! dont_panic { 52 | ($($x:tt)*) => ({ 53 | panic!($($x)*); 54 | }) 55 | } 56 | 57 | /// Like assert but calls `dont_panic!()` instead of `panic!()` 58 | #[macro_export] 59 | macro_rules! dp_assert { 60 | ($cond:expr) => ( 61 | if !$cond { 62 | dont_panic!(concat!("assertion failed: ", stringify!($cond))) 63 | } 64 | ); 65 | 66 | ($cond:expr, $($arg:tt)+) => ( 67 | if !$cond { 68 | dont_panic!($($arg)+) 69 | } 70 | ); 71 | } 72 | 73 | /// This function calls the given closure, asserting that there's no possibility of panicking. 74 | /// If the compiler can't prove this, the code will be left with a `dont_panic!` linking error. 75 | #[cfg(not(feature = "panic"))] 76 | pub fn call T>(f: F) -> T { 77 | struct DontPanic; 78 | impl Drop for DontPanic { 79 | fn drop(&mut self) { 80 | dont_panic!(); 81 | } 82 | } 83 | 84 | let guard = DontPanic; 85 | let result = f(); 86 | core::mem::forget(guard); 87 | result 88 | } 89 | 90 | /// With the `panic` feature turned on, this function just calls the closure directly, letting 91 | /// it panic or not on its own. 92 | #[cfg(feature = "panic")] 93 | pub fn call T>(f: F) -> T { 94 | f() 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | #[test] 100 | fn it_works() { 101 | let should_panic = false; 102 | if should_panic { 103 | dont_panic!(); 104 | } 105 | } 106 | 107 | #[test] 108 | fn call_slice_index() { 109 | let foo = [1, 2, 3]; 110 | super::call(|| assert_eq!(foo[0] + foo[1] + foo[2], 6)); 111 | } 112 | 113 | #[cfg(feature = "panic")] 114 | #[test] 115 | #[should_panic] 116 | fn panic() { 117 | let should_panic = true; 118 | if should_panic { 119 | dont_panic!(); 120 | } 121 | } 122 | 123 | #[cfg(feature = "panic")] 124 | #[test] 125 | fn no_panic() { 126 | let should_panic = false; 127 | if should_panic { 128 | dont_panic!(); 129 | } 130 | } 131 | 132 | #[cfg(feature = "panic")] 133 | #[test] 134 | #[should_panic] 135 | fn call_slice_index_panic() { 136 | let foo = [1, 2, 3]; 137 | super::call(|| assert_eq!(foo[1] + foo[2] + foo[3], 6)); 138 | } 139 | } 140 | --------------------------------------------------------------------------------