Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 1 | // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| 2 | // Exceptions. See /LICENSE for license information. |
| 3 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 4 | |
| 5 | // pathological shadowed names: shadow important modules that the macros use. |
| 6 | mod std {} |
| 7 | mod forward_declare {} |
| 8 | |
| 9 | mod test_is_same_0 { |
Devin Jeanpierre | 12dfb9a | 2023-01-18 14:54:41 -0800 | [diff] [blame] | 10 | type _Expected = ::forward_declare::internal::Symbol<()>; |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 11 | fn _is_same(x: _Expected) -> ::forward_declare::symbol!("") { |
| 12 | x |
| 13 | } |
| 14 | } |
| 15 | |
| 16 | mod test_is_same_1 { |
Devin Jeanpierre | 12dfb9a | 2023-01-18 14:54:41 -0800 | [diff] [blame] | 17 | type _Expected = ::forward_declare::internal::Symbol<(::forward_declare::internal::C<'x'>,)>; |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 18 | fn _is_same(x: _Expected) -> ::forward_declare::symbol!("x") { |
| 19 | x |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | mod test_is_same_3 { |
Devin Jeanpierre | 12dfb9a | 2023-01-18 14:54:41 -0800 | [diff] [blame] | 24 | type _Expected = ::forward_declare::internal::Symbol<( |
| 25 | ::forward_declare::internal::C<'f'>, |
| 26 | ::forward_declare::internal::C<'o'>, |
| 27 | ::forward_declare::internal::C<'o'>, |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 28 | )>; |
| 29 | fn _is_same(x: _Expected) -> ::forward_declare::symbol!("foo") { |
| 30 | x |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | #[test] |
| 35 | fn test_conversions() { |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 36 | use ::forward_declare::CcCast as _; // test becomes too verbose otherwise. |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 37 | struct MyType; |
| 38 | type MyTypeSymbol = ::forward_declare::symbol!("X"); |
| 39 | ::forward_declare::unsafe_define!(MyTypeSymbol, MyType); |
| 40 | |
| 41 | let mut complete = MyType; |
| 42 | ::forward_declare::forward_declare!(MyTypeIncomplete = MyTypeSymbol); |
| 43 | |
| 44 | fn ptr_location(x: impl ::std::ops::Deref) -> usize { |
| 45 | &*x as *const _ as *const u8 as usize |
| 46 | } |
| 47 | |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 48 | let loc = ptr_location(&complete); |
| 49 | |
| 50 | // & -> & |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 51 | { |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 52 | let incomplete_ref: &MyTypeIncomplete = (&complete).cc_cast(); |
| 53 | let complete_ref: &MyType = incomplete_ref.cc_cast(); |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 54 | assert_eq!(ptr_location(incomplete_ref), ptr_location(complete_ref)); |
| 55 | } |
| 56 | |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 57 | // Pin<&> <-> Pin<&> |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 58 | { |
| 59 | let incomplete_pin_ref: ::std::pin::Pin<&MyTypeIncomplete> = |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 60 | ::std::pin::Pin::new(&complete).cc_cast(); |
| 61 | let complete_pin_ref: ::std::pin::Pin<&MyType> = incomplete_pin_ref.cc_cast(); |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 62 | assert_eq!(ptr_location(incomplete_pin_ref), loc); |
| 63 | assert_eq!(ptr_location(complete_pin_ref), loc); |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 64 | let complete_unpinned_ref: &MyType = incomplete_pin_ref.cc_cast(); |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 65 | assert_eq!(ptr_location(complete_unpinned_ref), loc); |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 66 | } |
| 67 | |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 68 | // Pin<&mut> <-> Pin<&mut> |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 69 | { |
| 70 | let incomplete_pin_mut: ::std::pin::Pin<&mut MyTypeIncomplete> = |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 71 | ::std::pin::Pin::new(&mut complete).cc_cast(); |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 72 | assert_eq!(ptr_location(&*incomplete_pin_mut), loc); |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 73 | let complete_pin_mut: ::std::pin::Pin<&mut MyType> = incomplete_pin_mut.cc_cast(); |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 74 | assert_eq!(ptr_location(complete_pin_mut), loc); |
| 75 | } |
| 76 | |
| 77 | { |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 78 | // &mut -> Pin<&mut> |
| 79 | let mut incomplete_pin_mut: ::std::pin::Pin<&mut MyTypeIncomplete> = |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 80 | (&mut complete).cc_cast(); |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 81 | assert_eq!(ptr_location(&*incomplete_pin_mut), loc); |
| 82 | // Pin<&mut> -> &mut |
| 83 | { |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 84 | let complete_unpinned_mut: &mut MyType = incomplete_pin_mut.as_mut().cc_cast(); |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 85 | assert_eq!(ptr_location(complete_unpinned_mut), loc); |
| 86 | } |
| 87 | // Pin<&mut> -> & |
| 88 | { |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 89 | let complete_unpinned_ref: &MyType = incomplete_pin_mut.as_ref().cc_cast(); |
Devin Jeanpierre | 97a2379 | 2022-04-19 08:17:26 -0700 | [diff] [blame] | 90 | assert_eq!(ptr_location(complete_unpinned_ref), loc); |
| 91 | } |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | /// Typeless location&length info for a slice. |
| 95 | fn slice_location<T>(slice: &[T]) -> (usize, usize) { |
| 96 | (slice.as_ptr() as usize, slice.len()) |
| 97 | } |
| 98 | |
Lukasz Anforowicz | 0e38532 | 2022-06-30 09:00:50 -0700 | [diff] [blame] | 99 | // Vec<&> <-> Vec<&> |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 100 | { |
| 101 | let complete_vec: Vec<&MyType> = vec![&complete]; |
| 102 | let loc = slice_location(&complete_vec); |
| 103 | |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 104 | let incomplete_vec: Vec<&MyTypeIncomplete> = complete_vec.cc_cast(); |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 105 | assert_eq!(slice_location(&incomplete_vec), loc); |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 106 | let complete_vec: Vec<&MyType> = incomplete_vec.cc_cast(); |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 107 | assert_eq!(slice_location(&complete_vec), loc); |
| 108 | } |
Lukasz Anforowicz | 0e38532 | 2022-06-30 09:00:50 -0700 | [diff] [blame] | 109 | |
| 110 | // &[&] <-> &[&] |
| 111 | { |
| 112 | let complete_vec: Vec<&MyType> = vec![&complete]; |
| 113 | let complete_slice: &[&MyType] = complete_vec.as_slice(); |
| 114 | let loc = slice_location(complete_slice); |
| 115 | |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 116 | let incomplete_slice: &[&MyTypeIncomplete] = complete_slice.cc_cast(); |
Lukasz Anforowicz | 0e38532 | 2022-06-30 09:00:50 -0700 | [diff] [blame] | 117 | assert_eq!(slice_location(incomplete_slice), loc); |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 118 | let complete_slice: &[&MyType] = incomplete_slice.cc_cast(); |
Lukasz Anforowicz | 0e38532 | 2022-06-30 09:00:50 -0700 | [diff] [blame] | 119 | assert_eq!(slice_location(complete_slice), loc); |
| 120 | } |
| 121 | |
| 122 | // [&; N] <-> [&; N] |
| 123 | { |
| 124 | let complete_array: [&MyType; 2] = [&complete, &complete]; |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 125 | let incomplete_array: [&MyTypeIncomplete; 2] = complete_array.cc_cast(); |
Lukasz Anforowicz | 0e38532 | 2022-06-30 09:00:50 -0700 | [diff] [blame] | 126 | // TODO(jeanpierreda, lukasza): Avoid copying the array to a different memory location |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 127 | // (maybe by tweaking `cc_cast()` to use `std::mem::transmute` |
Lukasz Anforowicz | 0e38532 | 2022-06-30 09:00:50 -0700 | [diff] [blame] | 128 | // instead of `std::mem::transmute_copy` when the input and output types |
| 129 | // are both `Sized`). Once that is done, we should be able to add |
| 130 | // asserts that say: |
| 131 | // |
| 132 | // let loc = slice_location(&complete_array); |
| 133 | // ... |
| 134 | // assert_eq!(slice_location(&incomplete_array), loc) |
| 135 | // ... |
| 136 | // assert_eq!(slice_location(&complete_array), loc) |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 137 | let _complete_array: [&MyType; 2] = incomplete_array.cc_cast(); |
Lukasz Anforowicz | 0e38532 | 2022-06-30 09:00:50 -0700 | [diff] [blame] | 138 | } |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | /// You should be able to call unsafe_define!() twice (on different types) in |
| 142 | /// the same scope. |
| 143 | #[test] |
| 144 | fn test_hygiene() { |
| 145 | struct MyType1; |
| 146 | type MyTypeSymbol1 = ::forward_declare::symbol!("X1"); |
| 147 | ::forward_declare::unsafe_define!(MyTypeSymbol1, MyType1); |
| 148 | |
| 149 | struct MyType2; |
| 150 | type MyTypeSymbol2 = ::forward_declare::symbol!("X2"); |
| 151 | ::forward_declare::unsafe_define!(MyTypeSymbol2, MyType2); |
| 152 | } |
| 153 | |
| 154 | /// Suppose a library used to define its API using an incomplete type, but |
| 155 | /// changed to using a complete type? |
| 156 | /// This test verifies that callers continue to work as normal. |
| 157 | /// |
| 158 | /// (The reverse direction, fundamentally, is a lot less likely to work in |
| 159 | /// idiomatic code.) |
| 160 | #[test] |
| 161 | fn test_formerly_incomplete() { |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 162 | use ::forward_declare::CcCast as _; // test becomes too verbose otherwise. |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 163 | struct MyType; |
| 164 | ::forward_declare::unsafe_define!(::forward_declare::symbol!("X"), MyType); |
| 165 | |
| 166 | mod callee { |
| 167 | ::forward_declare::forward_declare!(pub MyType = ::forward_declare::symbol!("X")); |
| 168 | } |
| 169 | mod caller { |
| 170 | ::forward_declare::forward_declare!(pub MyType = ::forward_declare::symbol!("X")); |
| 171 | } |
| 172 | fn takes_incomplete(_: &callee::MyType) {} |
| 173 | fn takes_complete(_: &MyType) {} |
| 174 | |
| 175 | // calls which previously were converting a complete type to incomplete type are |
| 176 | // now converting a complete type to itself -- not great, but still compiles |
| 177 | // and works. |
| 178 | let x = MyType; |
| 179 | let x = &x; |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 180 | takes_incomplete(x.cc_cast()); // before |
| 181 | takes_complete(x.cc_cast()); // after |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 182 | |
Devin Jeanpierre | cfd8d4a | 2022-07-20 13:54:29 -0700 | [diff] [blame] | 183 | // Calls which previously were converting an incomplete type to an incomplete |
| 184 | // will also continue to work. In fact, this is required, since different crates |
| 185 | // will define different incomplete types. |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 186 | let x: &caller::MyType = x.cc_cast(); |
| 187 | takes_incomplete(x.cc_cast()); // before |
| 188 | takes_complete(x.cc_cast()); // after |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 189 | |
| 190 | // However, if you passed an incomplete type in without calling |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 191 | // .cc_cast(), that will no longer work. |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 192 | // takes_incomplete(x); // COMPILATION ERROR |
| 193 | // takes_complete(x); // COMPILATION ERROR |
| 194 | |
| 195 | // Symmetrically, you can also convert complete types to incomplete if all |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 196 | // callers call .cc_cast(), but this is much less reasonable a |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 197 | // requirement. |
| 198 | } |
| 199 | |
| 200 | /// In C++, you can define a type as so: |
| 201 | /// template<typename T> struct Vector {T* x; size_t length;}; |
| 202 | /// and it can be passed by value, even for forward-declared T. |
| 203 | /// How would this look in Rust? |
| 204 | /// |
| 205 | /// The aim of this test is to establish that we can define such a Vector that |
| 206 | /// supports conversion for complete/incomplete T, though being Rust, it cannot |
| 207 | /// be passed by *value*, only by *reference*. |
| 208 | /// |
| 209 | /// We can then add an additional trait bound on all methods, `where T : |
| 210 | /// Complete`. This turns out to be easier, for silly typing reasons, than |
| 211 | /// defining a whole new vector type for incomplete T. |
| 212 | #[test] |
| 213 | fn test_vector_alike() { |
Devin Jeanpierre | 12dfb9a | 2023-01-18 14:54:41 -0800 | [diff] [blame] | 214 | use ::forward_declare::{ |
| 215 | forward_declare, internal::CcType, symbol, unsafe_define, CcCast, Complete, |
| 216 | }; |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 217 | struct MyComplete; |
| 218 | unsafe_define!(symbol!("T"), MyComplete); |
| 219 | forward_declare!(MyIncomplete = symbol!("T")); |
| 220 | |
| 221 | /// An equivalent to Vector from the function comment, which is a compound |
| 222 | /// type that supports conversion. |
| 223 | struct Vector<T: ?Sized>(*mut T, usize); |
Lukasz Anforowicz | 511d3f8 | 2022-06-30 08:39:45 -0700 | [diff] [blame] | 224 | unsafe impl<T: ?Sized> CcType for Vector<T> |
| 225 | where |
| 226 | T: CcType, |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 227 | { |
Lukasz Anforowicz | 511d3f8 | 2022-06-30 08:39:45 -0700 | [diff] [blame] | 228 | type Name = (Vector<()>, T::Name); |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 229 | } |
| 230 | |
| 231 | /// Methods on `Vector` that don't require complete `T` |
| 232 | impl<T: ?Sized> Vector<T> { |
| 233 | fn len(&self) -> usize { |
| 234 | self.1 |
| 235 | } |
| 236 | } |
| 237 | /// Methods on Vector that require complete `T`. |
| 238 | impl<T: Complete> Vector<T> { |
| 239 | fn back(&self) -> Option<&T> { |
| 240 | if self.len() == 0 { |
| 241 | return None; |
| 242 | } |
| 243 | unimplemented!("An *actual* implementation is not important for this test"); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | fn expects_incomplete(_: &Vector<MyIncomplete>) {} |
| 248 | fn expects_complete(_: &Vector<MyComplete>) {} |
| 249 | |
| 250 | let complete = &Vector(0 as *mut MyComplete, 0); |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 251 | let incomplete: &Vector<MyIncomplete> = complete.cc_cast(); |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 252 | |
Lukasz Anforowicz | bdc54be | 2022-06-30 10:47:21 -0700 | [diff] [blame] | 253 | expects_incomplete(complete.cc_cast()); |
| 254 | expects_incomplete(incomplete.cc_cast()); |
| 255 | expects_complete(incomplete.cc_cast()); |
| 256 | expects_complete(complete.cc_cast()); |
Devin Jeanpierre | 3dd5f4d | 2022-03-31 02:18:36 -0700 | [diff] [blame] | 257 | |
| 258 | assert!(complete.back().is_none()); // works fine |
| 259 | // incomplete.back() // compilation error due to unsatisfied trait bounds |
| 260 | // (`!Complete`) |
| 261 | } |