1use core::ops::{Neg, Not};
2
3macro_rules! doc {
4 ($( $x:expr, )* @$item:item) => {
5 $( #[doc = $x] )*
6 $item
7 };
8}
9
10macro_rules! def_unary {
11 ($Op:ident, $op:ident, $RefOp:ident, $ref_op:ident) => {
12 mod $op {
13 pub trait Sealed {}
14 }
15
16 doc!(
17 concat!("`", stringify!($op), "` operation through mutable references."),
18 "",
19 "As of Rust 1.73.0, the following code does not compile:",
20 "```compile_fail",
21 concat!("use core::ops::", stringify!($Op), ";"),
22 "",
23 "struct A<T>(T);",
24 "",
25 concat!("impl<'a, T> ", stringify!($Op), " for &'a mut A<T>"),
26 "where",
27 concat!(" &'a mut T: ", stringify!($Op), ","),
28 "{",
29 concat!(" type Output = A<<&'a mut T as ", stringify!($Op), ">::Output>;"),
30 "",
31 concat!(" fn ", stringify!($op), "(self) -> Self::Output {"),
32 concat!(" A(self.0.", stringify!($op), "())"),
33 " }",
34 "}",
35 "",
36 "fn _f<T>(mut a: T)",
37 "where",
38 concat!(" for<'a> &'a mut T: ", stringify!($Op), ","),
39 "{",
40 concat!(" let _op_a = (&mut a).", stringify!($op), "();"),
41 "",
42 concat!(" // to do something with `a` and `_op_a`"),
43 "}",
44 "",
45 "fn _g<T>(a: T)",
46 "where",
47 concat!(" for<'a> &'a mut T: ", stringify!($Op), ","),
48 "{",
49 " _f(a);",
50 "}",
51 "```",
52 "but the following code does:",
53 "```",
54 concat!("use core::ops::", stringify!($Op), ";"),
55 concat!("use ref_ops::", stringify!($RefOp),";"),
56 "",
57 "struct A<T>(T);",
58 "", "",
59 concat!("impl<T> ", stringify!($Op), " for &mut A<T>"),
60 "where",
61 concat!(" T: ", stringify!($RefOp), ","),
62 "{",
63 " type Output = A<T::Output>;",
64 "",
65 concat!(" fn ", stringify!($op), "(self) -> Self::Output {"),
66 concat!(" A(self.0.", stringify!($ref_op), "())"),
67 " }",
68 "}",
69 "",
70 "fn _f<T>(mut a: T)",
71 "where",
72 concat!(" for<'a> &'a mut T: ", stringify!($Op), ","),
73 "{",
74 concat!(" let _op_a = (&mut a).", stringify!($op), "();"),
75 "",
76 concat!(" // to do something with `a` and `_op_a`"),
77 "}",
78 "",
79 "fn _g<T>(a: T)",
80 "where",
81 concat!(" for<'a> &'a mut T: ", stringify!($Op), ","),
82 "{",
83 " _f(a);",
84 "}",
85 "```",
86 @pub trait $RefOp: $op::Sealed {
87 doc!(
88 concat!("The resulting type after applying `", stringify!($op), "` operation."),
89 @type Output;
90 );
91
92 doc!(
93 concat!("Performs `", stringify!($op), "` operation."),
94 @fn $ref_op(&mut self) -> Self::Output;
95 );
96 }
97 );
98
99 impl<T, O> $op::Sealed for T
100 where
101 T: ?Sized,
102 for<'a> &'a mut T: $Op<Output = O>,
103 {
104 }
105
106 impl<T, O> $RefOp for T
107 where
108 T: ?Sized,
109 for<'a> &'a mut T: $Op<Output = O>,
110 {
111 type Output = O;
112
113 fn $ref_op(&mut self) -> O {
114 self.$op()
115 }
116 }
117 };
118}
119
120def_unary!(Neg, neg, RefMutNeg, ref_mut_neg);
121def_unary!(Not, not, RefMutNot, ref_mut_not);
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::{RefNeg, RefNot};
127
128 #[derive(PartialEq)]
129 struct B<T>(T);
130
131 macro_rules! impl_unary {
132 ($Op:ident, $op:ident, $RefOp:ident, $ref_op:ident) => {
133 impl<T> $Op for &mut B<T>
134 where
135 T: $RefOp,
136 {
137 type Output = B<T::Output>;
138
139 fn $op(self) -> Self::Output {
140 B(self.0.$ref_op())
141 }
142 }
143 };
144 }
145
146 impl_unary!(Neg, neg, RefNeg, ref_neg);
147 impl_unary!(Not, not, RefNot, ref_not);
148
149 macro_rules! test_unary {
150 ($fn:ident, $Op:ident, $op:ident, $RefOp:ident, $ref_op:ident, $assert:expr, $dummy:expr) => {
151 #[test]
152 fn $fn() {
153 #[derive(PartialEq)]
154 struct A<T: ?Sized>(T);
155
156 impl<T> $Op for &mut A<T>
157 where
158 T: ?Sized + $RefOp,
159 {
160 type Output = A<T::Output>;
161
162 fn $op(self) -> Self::Output {
163 A(self.0.$ref_op())
164 }
165 }
166
167 fn f<T>(mut a: T)
168 where
169 for<'a> &'a mut T: $Op,
170 {
171 let _op_a = (&mut a).$op();
172
173 }
175
176 fn g<T>(a: T)
177 where
178 for<'a> &'a mut T: $Op,
179 {
180 f(a);
181 }
182
183 g($dummy);
184
185 assert!($assert);
186 }
187 };
188 }
189
190 test_unary!(
191 test_neg,
192 Neg,
193 neg,
194 RefMutNeg,
195 ref_mut_neg,
196 -&mut A(B(1.0)) == A(B(-1.0)),
197 B(1.0)
198 );
199 test_unary!(
200 test_not,
201 Not,
202 not,
203 RefMutNot,
204 ref_mut_not,
205 !&mut A(B(true)) == A(B(false)),
206 B(true)
207 );
208}