Skip to content

Commit 16a5009

Browse files
unsafe_method unified. TODO docs, tests
1 parent 863e282 commit 16a5009

File tree

5 files changed

+113
-120
lines changed

5 files changed

+113
-120
lines changed

README.md

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,9 @@ As of late 2025, `const` traits are not stabilized in Rust. So, currently `unsaf
6969
name implying `const`, because this macro on its own **cannot** give a `const` guarantee.
7070

7171
```rust
72-
use prudent::unsafe_method_val;
73-
use prudent::unsafe_method_val as unsafe_method_for;
72+
use prudent::unsafe_method as unsafe_method_for;
7473

75-
// @TODO
74+
// @TODO accept path
7675
//
7776
//const ONE: u8 = unsafe_method_for!(1, u8::unchecked_add, );
7877
```
@@ -85,8 +84,8 @@ const ONE: u8 = unsafe_fn!(u8::unchecked_add, 1, 0);
8584

8685
# unsafe_method_ref
8786
```rust
88-
# use prudent::unsafe_method_ref;
89-
let _ = unsafe_method_ref!(1u8, unchecked_add, 0);
87+
# use prudent::unsafe_method;
88+
let _ = unsafe_method!(1u8, unchecked_add, 0);
9089

9190
struct SNonCopy {}
9291
impl SNonCopy {
@@ -96,14 +95,14 @@ impl SNonCopy {
9695
}
9796

9897
let s = SNonCopy {};
99-
unsafe_method_ref!(s, unsafe_method_no_args);
100-
unsafe_method_ref!(s, unsafe_method_one_arg, true);
101-
unsafe_method_ref!(s, unsafe_method_two_args, true, false);
98+
unsafe_method!(s, unsafe_method_no_args);
99+
unsafe_method!(s, unsafe_method_one_arg, true);
100+
unsafe_method!(s, unsafe_method_two_args, true, false);
102101
```
103102

104103
# unsafe_method_mut
105104
```rust
106-
# use prudent::unsafe_method_mut;
105+
# use prudent::unsafe_method;
107106
struct SNonCopy {}
108107
impl SNonCopy {
109108
fn unsafe_method_no_args(&mut self) {}
@@ -112,14 +111,14 @@ impl SNonCopy {
112111
}
113112

114113
let mut s = SNonCopy {};
115-
unsafe_method_mut!(s, unsafe_method_no_args);
116-
unsafe_method_mut!(s, unsafe_method_one_arg, true);
117-
unsafe_method_mut!(s, unsafe_method_two_args, true, false);
114+
unsafe_method!(s, unsafe_method_no_args);
115+
unsafe_method!(s, unsafe_method_one_arg, true);
116+
unsafe_method!(s, unsafe_method_two_args, true, false);
118117
```
119118

120119
# unsafe_method_val
121120
```rust
122-
# use prudent::unsafe_method_val;
121+
# use prudent::unsafe_method;
123122
{
124123
struct SNonCopy {}
125124
impl SNonCopy {
@@ -129,11 +128,11 @@ unsafe_method_mut!(s, unsafe_method_two_args, true, false);
129128
}
130129

131130
let sNonCopy = SNonCopy {};
132-
unsafe_method_val!(sNonCopy, unsafe_method_no_args);
131+
unsafe_method!(sNonCopy, unsafe_method_no_args);
133132
let sNonCopy = SNonCopy {};
134-
unsafe_method_val!(sNonCopy, unsafe_method_one_arg, true);
133+
unsafe_method!(sNonCopy, unsafe_method_one_arg, true);
135134
let sNonCopy = SNonCopy {};
136-
unsafe_method_val!(sNonCopy, unsafe_method_two_args, true, false);
135+
unsafe_method!(sNonCopy, unsafe_method_two_args, true, false);
137136
}
138137
{
139138
#[derive(Clone, Copy)]
@@ -143,8 +142,8 @@ unsafe_method_mut!(s, unsafe_method_two_args, true, false);
143142
}
144143

145144
let sCopy = SCopy {};
146-
unsafe_method_val!(sCopy, unsafe_method_no_args);
147-
unsafe_method_val!(sCopy, unsafe_method_no_args);
145+
unsafe_method!(sCopy, unsafe_method_no_args);
146+
unsafe_method!(sCopy, unsafe_method_no_args);
148147
let _ = sCopy;
149148
}
150149
```

simple_examples/method_one_postfix_param/Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "method_one_postfix_param"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
prudent = { version = "0.0.3-alpha", path="../.."}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use prudent::unsafe_method;
2+
3+
pub fn invoke() {
4+
let _ = unsafe_method!(1u8, unchecked_add, 0);
5+
}
6+
7+
/*
8+
pub fn invoke() {
9+
let _ = {
10+
if false {
11+
let (tuple_tree, receiver) = ((0,), 1u8);
12+
#[allow(unsafe_code)]
13+
unsafe {
14+
receiver.unchecked_add(tuple_tree.0)
15+
}
16+
::core::panicking::panic("internal error: entered unreachable code")
17+
} else {
18+
1u8.unchecked_add(0)
19+
}
20+
};
21+
}
22+
*/

src/lib.rs

Lines changed: 53 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ extern crate alloc;
9494
/// }
9595
/// };
9696
/// ```
97+
///
98+
/// This does NOT accept closures, since, as of Rust 1.91.0, closures cannot be `unsafe`.
9799
#[macro_export]
98100
macro_rules! unsafe_fn {
99101
( $fn:expr $(, $arg:expr)+ ) => {
@@ -184,108 +186,53 @@ macro_rules! unsafe_fn_internal_access_tuple_tree_field {
184186
}
185187
//-------------
186188

187-
// No need to seal this trait, as it's implemented for all types.
188-
/// Trait to accept/"normalize" a given receiver `self` from `T` to `&T`, or from `T` to `&mut T`.
189-
/// See [AsRefOrMut::prudent_normalize_value_self_as_ref].
190-
///
191-
/// Blanket implemented for all types.
192-
pub trait AsRefOrMut {
193-
/// Serves to accept/"normalize" a given receiver `self` that is of type `T`, `&T` or `&mut T`, so that
194-
/// the result is always of type `&T`. That way we can store its result in a local variable,
195-
/// even if `self` was passed in by value (rather than by reference) without moving it.
196-
///
197-
/// We call this method with the standard dot notation, and that's why it has such a long name,
198-
/// so it doesn't conflict with user-defined inherent methods or with other traits. We do NOT
199-
/// call this as `AsRefOrMutOrValue::prudent_normalize_value_self_as_ref(...)`, as that would
200-
/// require the receiver expression to be a reference (rather than allowing a value), and that's
201-
/// a restriction we want to prevent.
202-
///
203-
/// Used by [unsafe_method_ref].
204-
fn prudent_normalize_value_self_as_ref(&self) -> &Self {
205-
self
206-
}
207-
208-
/// Like [AsRefOrMut::prudent_normalize_value_self_as_ref], but this "normalizes" the given
209-
/// receiver to a mutable reference.
210-
///
211-
/// Used by [unsafe_method_mut].
212-
fn prudent_normalize_value_self_as_mut(&mut self) -> &mut Self {
213-
self
214-
}
215-
}
216-
impl<T> AsRefOrMut for T {}
217-
218-
/// Invoke an unsafe method that has a shared reference as a receiver: `&self`.
219-
///
220-
/// Like [unsafe_fn], but
221-
/// - This accepts a receiver `&self`, `&mut self` and `self` (which is then referenced, so it's
222-
/// **not** moved/copied).
223-
/// - This stores `self` in a variable outside of the generated `unsafe {...}`.
224-
/// - $fn can **NOT** be an expression or a path (which doesn't work in standard methods calls), but
225-
/// only an identifier.
226-
#[macro_export]
227-
macro_rules! unsafe_method_ref {
228-
($self:expr, $fn:ident $(, $arg:expr)* ) => {
229-
//$crate::unsafe_method_internal_normalize!{ $self, () (.prudent_normalize_value_self_as_ref()) $fn $(, $arg)* }
230-
231-
// const-friendly; if it creates a double reference &&, because the given expression already
232-
// yields a reference, that's OK. Rust will dereference it 2x.
233-
$crate::unsafe_method_internal_normalize!{ $self, (&) () $fn $(, $arg)* }
234-
}
235-
}
236-
237-
/// Like [unsafe_method_ref], but for methods whose receiver is a mutable reference: `&mut self`.
238-
#[macro_export]
239-
macro_rules! unsafe_method_mut {
240-
($self:expr, $fn:ident $(, $arg:expr)* ) => {
241-
// @TODO normal. prefix: &mut
242-
$crate::unsafe_method_internal_normalize!{ $self, () (.prudent_normalize_value_self_as_mut()) $fn $(, $arg)* }
243-
}
244-
}
245-
246-
/// Like [unsafe_method_ref], but for methods whose receiver is passed by value: `self` (that is,
247-
/// copied if it's [core::marker::Copy], or moved otherwise).
248-
#[macro_export]
249-
macro_rules! unsafe_method_val {
250-
($self:expr, $fn:ident $(, $arg:expr)* ) => {
251-
$crate::unsafe_method_internal_normalize!{ $self, () () $fn $(, $arg)* }
252-
}
253-
}
254-
255-
#[doc(hidden)]
189+
/// Invoke an `unsafe`` method. Like [unsafe_fn], but
190+
/// - This accepts a receiver `&self`, `&mut self` and `self`. TODO Box/Rc/Arc, dyn?
191+
/// - This treats `self` as if in an `unsafe {...}` block.
192+
/// - $fn can **NOT** be an expression or a qualified path (which doesn't work in standard methods
193+
/// calls anyways), but only an identifier.
256194
#[macro_export]
257-
/// - `$( $normalizer_suffix_part:tt )*` - an expression suffix that gets appended to $self (including any
258-
/// leading dot, if needed). For `unsafe_method_ref` and `unsafe_method`_it "normalizes" the given
259-
/// `$self`` to the type expected (shared reference `&self` or mutable reference `&mut`). For
260-
/// unsafe_method_val it's empty.
261-
macro_rules! unsafe_method_internal_normalize {
262-
($self:expr, ( $( $normalizer_prefix_part:tt )* ) ( $( $normalizer_suffix_part:tt )* ) $fn:ident $(, $arg:expr)+ ) => {
263-
// Enclosed in a block, so that
264-
// 1. the result can be used as a value in an outer expression,and
265-
// 2. local variables don't conflict with the outer scope
195+
macro_rules! unsafe_method {
196+
($self:expr, $fn:ident $(, $arg:expr)+ ) => {
197+
// Enclosed in a block, so that the result can be used as a value in an outer expression
198+
// without upsetting operator precedence.
266199
{
267-
use $crate::AsRefOrMut as _;
268-
let (tuple_tree, receiver) = (
269-
$crate::unsafe_fn_internal_build_tuple_tree!{ $($arg),+ },
270-
$( $normalizer_prefix_part )* ( $self ) $( $normalizer_suffix_part )*
271-
);
272-
$crate::unsafe_method_internal! {
273-
receiver,
274-
$fn,
275-
tuple_tree,
276-
( $( $arg ),* ),
277-
(0)
200+
if false {
201+
let (tuple_tree, mut receiver) = (
202+
$crate::unsafe_fn_internal_build_tuple_tree!{ $($arg),+ },
203+
$self
204+
);
205+
// Assign the result, in case the method is `#[must_use]`
206+
let _ = $crate::unsafe_method_internal! {
207+
receiver,
208+
$fn,
209+
tuple_tree,
210+
( $( $arg ),* ),
211+
(0)
212+
};
213+
unreachable!()
214+
} else {
215+
unsafe { $self. $fn ( $( $arg ),* ) }
278216
}
279217
}
280218
};
281219

282-
($self:expr, ( $( $normalizer_prefix_part:tt )* ) ( $( $normalizer_suffix_part:tt )* ) $fn:ident ) => {
220+
($self:expr, $fn:ident ) => {
221+
// Enclosed in a block, so that the result can be used as a value in an outer expression
222+
// without upsetting operator precedence.
283223
{
284-
use $crate::AsRefOrMut as _;
285-
let receiver = $( $normalizer_prefix_part )* ( $self ) $( $normalizer_suffix_part )*;
286-
#[allow(unsafe_code)]
287-
unsafe {
288-
receiver. $fn()
224+
if false {
225+
let mut receiver = $self;
226+
// Assign the result, in case the method is `#[must_use]`
227+
let _ = {
228+
#[allow(unsafe_code)]
229+
unsafe {
230+
receiver. $fn()
231+
}
232+
};
233+
unreachable!()
234+
} else {
235+
$self. $fn ()
289236
}
290237
}
291238
};
@@ -318,12 +265,17 @@ macro_rules! unsafe_method_internal {
318265
)
319266
),*
320267
) => {
321-
#[allow(unsafe_code)]
322-
unsafe {
323-
$self. $fn( $(
324-
$crate::unsafe_fn_internal_access_tuple_tree_field!{ $tuple_tree, $($accessor_part),+ }
325-
),*
326-
)
268+
// Extra block needed, in case the result is assigned to a variable. Otherwise surprise:
269+
// "attributes on expressions are experimental"
270+
// https://github.com/rust-lang/rust/issues/15701
271+
{
272+
#[allow(unsafe_code)]
273+
unsafe {
274+
$self. $fn( $(
275+
$crate::unsafe_fn_internal_access_tuple_tree_field!{ $tuple_tree, $($accessor_part),+ }
276+
),*
277+
)
278+
}
327279
}
328280
};
329281
}
@@ -476,4 +428,3 @@ macro_rules! unsafe_set {
476428
//
477429
// #![feature(stmt_expr_attributes)]
478430
//
479-
// (#[deny(unsafe_code)] s ).prudent_normalize_value_self_as_mut();

0 commit comments

Comments
 (0)