11#![ doc = include_str ! ( "../README.md" ) ]
2- // @TODO nightly?
3- //
4- // https://github.com/rust-lang/rust/issues/143874
5- //
6- // #![feature(const_trait_impl)]
72#![ cfg_attr( not( any( doc, test) ) , no_std) ]
83// This only refuses unsafe code in functions/expressions in this crate, but it ignores any unsafe
94// code generated by this crate's macros. (https://github.com/rust-lang/nomicon/issues/506)
5348#[ cfg( doc) ]
5449extern crate alloc;
5550
56- // Implementation notes ARE a part of the documentation:
57- // - Otherwise it's a pain to edit them.
58- // - Users deserve documentation of how a macro works, because
59- // - macros are much more difficult to read than Rust non-macro code, and
60- // - macros inject code evaluated as if written by the user, so potential for a security problem,
61- // even without any `unsafe` code.
62- //
63- /// Invoke an `unsafe` function.
51+ /// Invoke an `unsafe` function, but isolate `unsafe {...}` only for the function invocation itself.
52+ /// - `If $fn`, that is, the function itself, is NOT given as an identifier/qualified path, but it's
53+ /// given as an expression, then this expression is treated as if evaluated **outside** `unsafe
54+ /// {...}`.
55+ /// - Any arguments passed in as expressions are treated as if evaluated **outside** `unsafe {...}`.
6456///
6557/// There is **no** extra enclosing pair of parenthesis `(...)` around the list of arguments (if
6658/// any). If there was such a pair, it could be confused for a tuple. It would also be less readable
6759/// when some parameters were tuples/complex expressions.
6860///
69- /// Implementation notes
70- ///
71- /// We want to accept any valid expressions for function parameters. But we don't want them to
72- /// evaluate inside `unsafe {...}` block that calls the given `unsafe` function. That could "hide"
73- /// other `unsafe` code (in expressions passed as parameters). So, we assign the given expressions
74- /// (for parameters) to local variables first, before `unsafe{...}`.
75- ///
76- /// However, that is a challenge with `macro_rules!`:
77- /// - variable number of parameters => we store them in a tuple; but
78- /// - storing them in an ordinary "flat" tuple would involve index numbers 0, 1, 2... That is
79- /// difficult to generate with `macro_rules!` so that the generated numbers are literals:
80- /// - It is possible for `macro_rules!` to generate integer **expressions**, but even that is
81- /// [tricky](https://lukaswirth.dev/tlborm/decl-macros/building-blocks/counting.html).
82- /// - Even if we generated integer expressions, we can't use them to access tuple fields. Instead,
83- /// tuple access works with integer literals only.
84- ///
85- /// Solution: A "tuple tree". It's an unbalanced binary tree. See it expanded:
86- /// - [simple_examples/fn_add_three/src/main.rs](https://github.com/peter-lyons-kehl/prudent/blob/main/simple_examples/fn_add_three/src/main.rs)
87- /// - `cd simple_examples/fn_add_three`
88- /// - `cargo expand`
89- ///
90- /// // @TODO update/remove - replace with links to the stored output of `cargo expand`
91- /// ```rust
92- /// # use prudent::unsafe_fn;
93- /// unsafe fn add_three(left: u64, middle: u64, right: u64) -> u64 {
94- /// left + middle + right
95- /// }
96- /// unsafe_fn!(add_three, 1, 2, 3);
97- /// ```
98- /// expands to:
99- /// ```rust
100- /// # unsafe fn add_three(left: u64, middle: u64, right: u64) -> u64 {
101- /// # left + middle + right
102- /// # }
103- /// // ...
104- /// {
105- /// let (tuple, fun) = ( (1, (2, (3,))), add_three );
106- /// #[allow(unsafe_code)]
107- /// unsafe {
108- /// fun(tuple.0, tuple.1 .0, tuple.1 .1 .0)
109- /// }
110- /// };
111- /// ```
112- /// and
113- /// [simple_examples/fn_add_four/src/main.rs](https://github.com/peter-lyons-kehl/prudent/blob/main/simple_examples/fn_add_four/src/main.rs):
114- /// ```rust
115- /// # use prudent::unsafe_fn;
116- /// unsafe fn add_four(left: u64, middle_left:u64, middle_right:u64, right: u64) -> u64 {
117- /// left + middle_left + middle_right + right
118- /// }
119- /// unsafe_fn!(add_four, 1, 2, 3, 4);
120- /// ```
121- /// expands to:
122- /// ```rust
123- /// # unsafe fn add_four(left: u64, middle_left: u64, middle_right: u64, right: u64) -> u64 {
124- /// # left + middle_left + middle_right + right
125- /// # }
126- /// // ...
127- /// {
128- /// let (tuple_tree, fun) = ( (1, (2, (3, (4,)))), add_four );
129- /// #[allow(unsafe_code)]
130- /// unsafe {
131- /// fun(
132- /// tuple_tree.0,
133- /// tuple_tree.1 .0,
134- /// tuple_tree.1 .1 .0,
135- /// tuple_tree.1 .1 .1 .0,
136- /// )
137- /// }
138- /// };
139- /// ```
140- ///
14161/// @TODO replace with examples/ :
14262/// ```
14363#[ doc = include_str ! ( "../simple_examples/fn_add_three/src/main.rs" ) ]
@@ -164,7 +84,7 @@ macro_rules! unsafe_fn {
16484
16585/// Invoke an `unsafe` method. Like [unsafe_fn], but
16686/// - This accepts a receiver `&self`, `&mut self` and `self`. TODO Box/Rc/Arc, dyn?
167- /// - This treats `self` as if in an `unsafe {...}` block.
87+ /// - This treats `self` as if it were evaluated **outside** the `unsafe {...}` block.
16888/// - $fn can **NOT** be an expression or a qualified path (which doesn't work in standard methods
16989/// calls anyways), but only an identifier.
17090#[ macro_export]
@@ -186,7 +106,8 @@ macro_rules! unsafe_method {
186106}
187107//-------------
188108
189- /// Set a value of a `static mut` variable or its (sub...-)field.
109+ /// Set a value of a `static mut` variable or its (sub...-)field, but isolate `unsafe {...}` only to
110+ /// that assignment.
190111///
191112/// To minimize unintended `unsafe`, calculate any indexes etc. beforehand, store them in local
192113/// variables and pass them in.
@@ -196,29 +117,31 @@ macro_rules! unsafe_method {
196117#[ macro_export]
197118macro_rules! unsafe_static_set {
198119 ( $static: expr, $val: expr) => { {
199- let val = $val;
200- #[ allow( unsafe_code) ]
201- unsafe {
202- $static = val;
120+ if false {
121+ let _ = $val;
122+ unreachable!( )
123+ } else {
124+ #[ allow( unsafe_code) ]
125+ unsafe {
126+ $static = $val;
127+ }
203128 }
204129 } } ;
205130}
206131
207132/// Deref a pointer (either `const` or `mut`) and yield a read-only reference.
208133///
209- /// If `$type` is given, it's expected to be the referenced type (NOT the given pointer, NOT the
210- /// target reference type) and the given pointer is cast to `* const $type`. `$type` may start with
211- /// `dyn`. `$type` may be a slice `[...]`.
134+ /// If `$type` is given, it's expected to be the referenced type (NOT the given pointer, NOT a
135+ /// reference based on the given pointer), and the given pointer is cast to `* const $type`. `$type`
136+ /// may start with `dyn`. `$type` may be a slice `[...]`.
212137#[ macro_export]
213138macro_rules! unsafe_ref {
214139 ( $ptr: expr) => { {
215- let ptr = $ptr;
216- let _: * const _ = ptr; // Partial type check that $ptr yields a const pointer
140+ let ptr: * const _ = $ptr;
217141 unsafe { & * ptr }
218142 } } ;
219143 ( $ptr: expr, $lifetime: lifetime) => { {
220- let ptr = $ptr;
221- let _: * const _ = ptr; // Partial type check that $ptr yields a const pointer
144+ let ptr: * const _ = $ptr;
222145 unsafe { & * ptr as & $lifetime _ }
223146 } } ;
224147 ( $ptr: expr, $type: ty) => { {
@@ -241,25 +164,21 @@ macro_rules! unsafe_ref {
241164#[ macro_export]
242165macro_rules! unsafe_mut {
243166 ( $ptr: expr) => { {
244- let ptr = $ptr;
245- let _: * mut _ = ptr; // Partial type check that $ptr yields a mut pointer
167+ let ptr: * mut _ = $ptr;
246168 unsafe { & mut * ptr }
247169 } } ;
248170 ( $ptr: expr, $lifetime: lifetime) => { {
249- let ptr = $ptr;
250- let _: * mut _ = ptr; // Partial type check that $ptr yields a mut pointer
171+ let ptr: * mut _ = $ptr;
251172 unsafe { & mut * ptr as & $lifetime mut _}
252173 } } ;
253174 ( $ptr: expr, $ptr_type: ty) => { {
254175 let ptr = $ptr;
255176 let ptr = ptr as * mut $ptr_type;
256- // let _: *mut _ = ptr; // Partial type check that $ptr yields a mut pointer
257177 unsafe { & mut * ptr}
258178 } } ;
259179 ( $ptr: expr, $ptr_type: ty, $lifetime: lifetime) => { {
260180 let ptr = $ptr;
261181 let ptr = ptr as * mut $ptr_type;
262- //let _: *mut _ = ptr; // Partial type check that $ptr yields a mut pointer
263182 unsafe { & mut * ptr as & $lifetime mut _}
264183 } } ;
265184}
@@ -274,15 +193,13 @@ pub const fn expect_copy_ptr<T: Copy>(_: *const T) {}
274193#[ macro_export]
275194macro_rules! unsafe_val {
276195 ( $ptr: expr) => { {
277- let ptr = $ptr;
278- let _: * const _ = ptr; // Partial type check that $ptr yields a const pointer
196+ let ptr: * const _ = $ptr;
279197 $crate:: expect_copy_ptr( ptr) ;
280198 unsafe { * ptr }
281199 } } ;
282200 ( $ptr: expr, $ptr_type: ty) => { {
283201 let ptr = $ptr;
284202 let ptr = ptr as * const $ptr_type;
285- let _: * const _ = ptr; // Partial type check that $ptr yields a const pointer
286203 $crate:: expect_copy_ptr( ptr) ;
287204 unsafe { * ptr }
288205 } } ;
@@ -319,11 +236,15 @@ macro_rules! unsafe_use {
319236#[ macro_export]
320237macro_rules! unsafe_set {
321238 ( $ptr: expr, $value: expr) => { {
322- let ( ptr, value) = ( $ptr, $value) ;
323- let _: * mut _ = ptr; // Partial type check that $ptr yields a mut pointer
324- #[ allow( unsafe_code) ]
325- unsafe {
326- * ptr = value;
239+ if false {
240+ let _: * mut _ = $ptr;
241+ let _ = $value;
242+ unreachable!( )
243+ } else {
244+ #[ allow( unsafe_code) ]
245+ unsafe {
246+ * $ptr = $value;
247+ }
327248 }
328249 } } ;
329250}
0 commit comments