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 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 A<T>"),
26 "where",
27 concat!(" &'a T: ", stringify!($Op), ","),
28 "{",
29 concat!(" type Output = A<<&'a 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>(a: T)",
37 "where",
38 concat!(" for<'a> &'a T: ", stringify!($Op), ","),
39 "{",
40 concat!(" let _op_a = (&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 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 &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>(a: T)",
71 "where",
72 concat!(" for<'a> &'a T: ", stringify!($Op), ","),
73 "{",
74 concat!(" let _op_a = (&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 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(&self) -> Self::Output;
95 );
96 }
97 );
98
99 impl<T, O> $op::Sealed for T
100 where
101 T: ?Sized,
102 for<'a> &'a T: $Op<Output = O>,
103 {
104 }
105
106 impl<T, O> $RefOp for T
107 where
108 T: ?Sized,
109 for<'a> &'a T: $Op<Output = O>,
110 {
111 type Output = O;
112
113 fn $ref_op(&self) -> O {
114 self.$op()
115 }
116 }
117 };
118}
119
120def_unary!(Neg, neg, RefNeg, ref_neg);
121def_unary!(Not, not, RefNot, ref_not);
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 macro_rules! test_unary {
128 ($fn:ident, $Op:ident, $op:ident, $RefOp:ident, $ref_op:ident, $assert:expr, $dummy:literal) => {
129 #[test]
130 fn $fn() {
131 #[derive(PartialEq)]
132 struct A<T: ?Sized>(T);
133
134 impl<T> $Op for &A<T>
135 where
136 T: ?Sized + $RefOp,
137 {
138 type Output = A<T::Output>;
139
140 fn $op(self) -> Self::Output {
141 A(self.0.$ref_op())
142 }
143 }
144
145 fn f<T>(a: T)
146 where
147 for<'a> &'a T: $Op,
148 {
149 let _op_a = (&a).$op();
150
151 }
153
154 fn g<T>(a: T)
155 where
156 for<'a> &'a T: $Op,
157 {
158 f(a);
159 }
160
161 g($dummy);
162
163 assert!($assert);
164 }
165 };
166 }
167
168 test_unary!(
169 test_neg,
170 Neg,
171 neg,
172 RefNeg,
173 ref_neg,
174 -&A(1.0) == A(-1.0),
175 1.0
176 );
177 test_unary!(
178 test_not,
179 Not,
180 not,
181 RefNot,
182 ref_not,
183 !&A(true) == A(false),
184 true
185 );
186}