Devin Jeanpierre | b347dd3 | 2024-11-22 12:55:55 -0800 | [diff] [blame] | 1 | # Unstable Features |
| 2 | |
| 3 | ## Unstable features used by Crubit {#accepted} |
| 4 | |
| 5 | For each feature we use, we document the following: |
| 6 | |
| 7 | * **Crubit feature:** Under which Crubit feature flags do we use this? If it |
| 8 | is under `supported`, then the Rust feature breaking can break Crubit users |
| 9 | in general. If it is under `experimental`, then it only breaks tests and |
| 10 | close partner teams. |
Devin Jeanpierre | 181070f | 2024-11-22 13:47:41 -0800 | [diff] [blame] | 11 | * **Use case:** What do we use the feature for? |
Devin Jeanpierre | b347dd3 | 2024-11-22 12:55:55 -0800 | [diff] [blame] | 12 | * **Exit strategy:** What would happen if the feature went away? |
| 13 | |
| 14 | ### `custom_inner_attributes` |
| 15 | |
| 16 | * **Crubit feature:** `supported` |
| 17 | * **Use case:** Used to suppress automatic formatting, to make our golden |
| 18 | tests stable even in the face of e.g. non-idempotency bugs in `rustfmt`. |
| 19 | * **Exit strategy:** Disable `rustfmt` on the golden tests some other way. |
| 20 | |
| 21 | ### `negative_impls` |
| 22 | |
| 23 | * **Crubit feature:** `supported` |
| 24 | * **Use case:** Used to implement `ctor` / nontrivial intialization, so that |
| 25 | we can dispatch on the existence of the `Unpin` trait (which is not possible |
| 26 | with `PhantomPinned`). Also used so that we can pin/unpin a type without |
| 27 | adding a field (which is not possible with `PhantomPinned`). |
| 28 | * **Exit strategy:** For dispatch, we can define a new auto trait (also an |
| 29 | unstable feature) or use specialization (also an unstable feature). For the |
| 30 | fields, this is a compatibility break, and we'd need to add a PhantomData |
| 31 | field to all C++ types to mark them as `!Send`, `!Unpin`, etc. |
| 32 | |
Devin Jeanpierre | b347dd3 | 2024-11-22 12:55:55 -0800 | [diff] [blame] | 33 | ### `vec_into_raw_parts` |
| 34 | |
| 35 | * **Crubit feature:** `experimental` |
| 36 | * **Use case:** Used for conversions of vectors of forward-declared |
| 37 | objects, which is not yet released. |
| 38 | * **Exit strategy:** We could delete this if we had to. Hopefully low-risk, |
| 39 | people love raw parts. |
| 40 | |
| 41 | ### `extern_types` |
| 42 | |
| 43 | * **Crubit feature:** `experimental` |
| 44 | * **Use case:** Used for forward declarations, which have an unknown size. |
| 45 | * **Exit strategy:** Hard to avoid: without this, we get UB for forward |
| 46 | declarations if you use references, due to provenance rules. Don't need the |
| 47 | `forward_declare` crate except for migration from existing C++ code to Rust, |
| 48 | where it relies on forward declarations for build performance or |
| 49 | cycle-breaking reasons. |
| 50 | |
| 51 | ### `arbitrary_self_types` |
| 52 | |
| 53 | * **Crubit feature:** `experimental` |
| 54 | * **Use case:** We need this or an equivalent feature in order to make methods |
| 55 | callable on rvalue references but not regular const references, so as to |
| 56 | support C++ move semantics. Also likely need it for aliasing-safe |
| 57 | references. |
| 58 | * **Exit strategy:** Likely can't live without this feature. Lowish risk, in |
| 59 | that Rust needs user-defined types to be usable as self types (e.g. it |
| 60 | already has this for Rc), although the exact mechanism is as-yet undecided. |
| 61 | |
| 62 | ### `never_type` |
| 63 | |
| 64 | * **Crubit feature:** `experimental` |
| 65 | * **Use case:** Used in a test to demonstrate we don't support it. |
| 66 | * **Exit strategy:** Delete test. |
| 67 | |
| 68 | ### `c_variadic` |
| 69 | |
| 70 | * **Crubit feature:** `experimental` |
| 71 | * **Use case:** Used in a test to demonstrate we don't support it. |
| 72 | * **Exit strategy:** Delete test. |
| 73 | |
| 74 | ### `abi_vectorcall` |
| 75 | |
| 76 | * **Crubit feature:** `experimental` |
| 77 | * **Use case:** Used in a test to test non-C calling conventions. |
| 78 | * **Exit strategy:** Delete test at worst, replace with different non-C |
| 79 | calling convention at best. |
| 80 | |
| 81 | ### `impl_trait_in_assoc_type` |
| 82 | |
| 83 | * **Crubit feature:** `experimental` |
| 84 | * **Use case:** Used for returning `impl Ctor` for nontrivial construction. |
| 85 | * **Exit strategy:** Replace with |
| 86 | [RPITIT](https://github.com/rust-lang/rfcs/pull/3425), which is stable now. |
| 87 | |
| 88 | ### `allocator_api` |
| 89 | |
| 90 | * **Crubit feature:** unreleased |
| 91 | * **Use case:** Used for an ABI-compatible implementation of `std::vector` |
| 92 | that can reuse the C++ allocator. |
| 93 | * **Exit strategy:** Do not release, or release but require that the Rust |
| 94 | global allocator *is* the C++ allocator. |
| 95 | |
| 96 | ### `cfg_sanitize` |
| 97 | |
| 98 | * **Crubit feature:** unreleased |
| 99 | * **Use case:** Used for an ABI-compatible implementation of `std::vector` |
| 100 | that poisons memory in the same way as libc++. |
| 101 | * **Exit strategy:** Do not release, or release but with degraded |
| 102 | AddressSanitizer results that hide bugs. |
| 103 | |
Krasimir Georgiev | 2b50e12 | 2025-03-13 07:54:41 -0700 | [diff] [blame] | 104 | ### `cfg_accessible`, `stmt_expr_attributes`, `proc_macro_hygiene` |
Devin Jeanpierre | 831def3 | 2025-02-04 06:26:32 -0800 | [diff] [blame] | 105 | |
| 106 | * **Crubit feature:** N/A, used internally by Crubit |
| 107 | * **Use case:** Crubit uses rustc as a library, which is subject to |
| 108 | backwards-incompatible changes. `cfg_accessible` makes it much easier to |
| 109 | write code which is compatible with two versions of rustc at the same time. |
Krasimir Georgiev | 2b50e12 | 2025-03-13 07:54:41 -0700 | [diff] [blame] | 110 | `stmt_expr_attributes`, `proc_macro_hygiene` lets us handle |
| 111 | backwards-incompatible changes affecting individual expressions. |
Devin Jeanpierre | 831def3 | 2025-02-04 06:26:32 -0800 | [diff] [blame] | 112 | * **Exit strategy:** If `cfg_accessible` changes during the very same update |
| 113 | we are trying to be compatible with, we can't use it in the first place. If |
| 114 | it changes in a future version, then the update has already completed |
| 115 | successfully, so we can delete the usage (keeping the `cfg_accessible` usage |
Krasimir Georgiev | 2b50e12 | 2025-03-13 07:54:41 -0700 | [diff] [blame] | 116 | that is currently used, and deleting the other one). Same for the rest. |
Devin Jeanpierre | 831def3 | 2025-02-04 06:26:32 -0800 | [diff] [blame] | 117 | |
Devin Jeanpierre | d446009 | 2025-06-10 23:28:41 -0700 | [diff] [blame] | 118 | ### `unsized_const_params` |
| 119 | |
| 120 | * **Crubit feature:** `supported` |
| 121 | * **Use case:** Better error messages when you get a type error on forward |
| 122 | declarations. `Symbol<(C<'F'>, C<'o'>, C<'o'>)>` is so substantially worse |
| 123 | than `Symbol<"Foo">` that Rust will even hide the type entirely, forcing you |
| 124 | to read the name from a separate file (which may not be available to you if |
| 125 | you were doing a remote build). |
| 126 | * **Exit strategy:** The `Symbol<(...)>` approach works and is tested, and we |
| 127 | can switch back with a single flag flip. The only impact is a regression on |
| 128 | error messages. |
| 129 | |
Devin Jeanpierre | b347dd3 | 2024-11-22 12:55:55 -0800 | [diff] [blame] | 130 | ## Unstable features **not** used by Crubit {#rejected} |
| 131 | |
| 132 | The following features are ones we'd hypothetically like to use, but do not. |
| 133 | |
Devin Jeanpierre | 181070f | 2024-11-22 13:47:41 -0800 | [diff] [blame] | 134 | ### `try_trait_v2` |
Devin Jeanpierre | b347dd3 | 2024-11-22 12:55:55 -0800 | [diff] [blame] | 135 | |
| 136 | Crubit does aim to support the Abseil error type, `absl::Status`, which ideally |
| 137 | would be usable everywhere a `Result` is for ergonomics. For example, we'd |
| 138 | expect you to be able to write code like the following: |
| 139 | |
| 140 | ```rust |
| 141 | pub fn read() -> Status { |
| 142 | foo()?; |
| 143 | } |
| 144 | ``` |
| 145 | |
| 146 | Where `foo` returns a `Status`, or even an `io::Result`. |
| 147 | |
| 148 | For this to work with `Status` being *exactly the same type* as it is in C++, |
| 149 | with the same layout, we need to use `try_trait_v2`. |
| 150 | |
| 151 | Until it is stabilized, or the cost/benefit becomes worthwhile, we can work |
| 152 | around it by using `Result`, and converting when a `Result` is passed or |
| 153 | returned by value. |
| 154 | |
Devin Jeanpierre | 5279227 | 2025-01-30 14:52:59 -0800 | [diff] [blame] | 155 | ### `fn_traits` |
| 156 | |
| 157 | Much like `try_traits_v2`, we'd like to support C++ "function objects" which |
| 158 | implement the call operator. In Rust, without `fn_traits`, the API is |
| 159 | awkward: given a `foo` of type `std::function`, `absl::AnyInvocable`, or |
| 160 | `absl::FunctionRef`, you cannot just write `foo()` to call it, because it cannot |
| 161 | implement the `Fn*` family of traits. This makes these types less convenient to |
| 162 | use than they are in C++, requiring something like `foo.as_closure()()` to |
| 163 | create a closure out of a `FunctionRef` or similar. |
| 164 | |
| 165 | (Another use case we've examined in the past: in addition to implementing |
| 166 | function objects, `fn_traits` could also be used to implement overloading, |
| 167 | including overloaded function objects. For example, an overloaded function |
| 168 | could, instead, be a constant with many different implementations of `Fn`, for |
| 169 | different parameter types. Though, this ends up looking a bit |
| 170 | odd: `(mystruct.overloaded)()`) |
| 171 | |
Devin Jeanpierre | 181070f | 2024-11-22 13:47:41 -0800 | [diff] [blame] | 172 | ### `min_specialization` |
Devin Jeanpierre | b347dd3 | 2024-11-22 12:55:55 -0800 | [diff] [blame] | 173 | |
| 174 | We have many use cases for `min_specialization`, including bindings for C++ |
| 175 | templates that have explicit template specialization or partial template |
| 176 | specialization. However, they are not yet pressing, and specialization related |
| 177 | Rust features are especially risky. |
Devin Jeanpierre | 7fcc340 | 2025-02-20 18:11:34 -0800 | [diff] [blame] | 178 | |
| 179 | ### `inherent_associated_types` |
| 180 | |
| 181 | Inherent associated types give a natural Rust spelling of the following C++ |
| 182 | type definition: |
| 183 | |
| 184 | ```c++ |
| 185 | struct Foo { |
| 186 | using MyAlias = int; |
| 187 | }; |
| 188 | ``` |
Devin Jeanpierre | 29860b3 | 2025-03-25 16:57:45 -0700 | [diff] [blame] | 189 | |
| 190 | ### `disjoint_associated_types` |
| 191 | |
| 192 | [`disjoint_associated_types`](https://github.com/rust-lang/rfcs/pull/1672) was |
| 193 | closed, but not necessarily permanently. See also |
| 194 | https://github.com/rust-lang/rust/issues/20400. |
| 195 | |
| 196 | Not every C++ type supports the Rust move operation. For more on this, see |
| 197 | [Classes and Structs](../cpp/classes_and_structs#trivially_relocatable). |
| 198 | |
| 199 | Crubit (in `experimental` mode) supports passing and returning these non-Rust-movable C++ |
| 200 | objects by value. But since they are not Rust-movable, they cannot literally |
| 201 | be returned in Rust by value: `pub fn foo() -> X` performs a Rust move of its |
| 202 | return type by definition. |
| 203 | |
| 204 | Instead, these objects support lazy construction, in the same style as |
| 205 | [`moveit`](https://mcyoung.xyz/2021/04/26/move-ctors/). See |
| 206 | [`ctor.rs`](https://github.com/google/crubit/blob/main/support/ctor.rs). This |
| 207 | results in, for example, the following API differences: |
| 208 | |
| 209 | `X` is rust-movable | `X` is not rust-movable |
| 210 | -------------------- | --------------------------------------- |
| 211 | `pub fn foo() -> X` | `pub fn foo() -> impl Ctor<Output=X>` |
| 212 | `impl Add<X> for &C` | `impl<T: Ctor<Output=X>> Add<T> for &C` |
| 213 | |
| 214 | The problem comes in with operator overloading: the following is valid: |
| 215 | |
| 216 | ```rust |
| 217 | impl Add<X> for &C {...} |
| 218 | impl Add<Y> for &C {...} |
| 219 | ``` |
| 220 | But the equivalent using this lazy-construction trait is not: |
| 221 | |
| 222 | ```rust |
| 223 | impl<T: Ctor<Output=X>> Add<T> for &C {...} |
| 224 | impl<T: Ctor<Output=Y>> Add<T> for &C {...} |
| 225 | ``` |
| 226 | |
| 227 | Rust doesn't know that these two are disjoint, meaning that we cannot use the |
| 228 | `Ctor` trait approach for traits. |
| 229 | |
| 230 | An alternative fix would be language support for in-place pinned construction. |
| 231 | That would render the `Ctor` trait obsolete, and reduce Crubit's needs around |
| 232 | trait coherence (as well as `negative_impls`, above). |
Taylor Cramer | 7e125e8 | 2025-03-31 11:06:55 -0700 | [diff] [blame] | 233 | |
| 234 | |
| 235 | ### `register_tool` |
| 236 | |
| 237 | We previously used this attribute to allow Crubit to read annotations on |
| 238 | types/functions. However, the need for end-users to enable a feature gate and |
| 239 | re-declare the tool caused us to migrate away to using `#[doc]` attributes. |
| 240 | |
| 241 | We use custom attributes so that Crubit can round-trip a type correctly, or to |
| 242 | implement automated bridging so that a C++ `Status` becomes a Rust `Result<(), |
Devin Jeanpierre | 3e1c00e | 2025-04-23 17:29:48 -0700 | [diff] [blame] | 243 | StatusError>`, or what have you. |
| 244 | |
| 245 | ### `super_let` |
| 246 | |
| 247 | Crubit's `ctor.rs` crate uses a syntax like `emplace!{let x = ...}` to in-place |
| 248 | initialize a pinned value using a C++ constructor. It cannot use the same trick |
| 249 | that `pin!()` does, because it needs to actually call a function to initialize |
| 250 | the memory, and so lifetime extension would not apply: the memory, being passed |
| 251 | to a function, would have its lifetime end at the end of the statement. |
| 252 | |
| 253 | `super_let` would allow us to instead write `let x = emplace!(...)`, with the |
| 254 | macro expanding to a use of `super` to lifetime-extend the storage |
| 255 | subexpression. |
| 256 | |
| 257 | For example: |
| 258 | |
| 259 | ```rust |
| 260 | macro_rules! emplace { |
| 261 | ($expr:expr) => { |
| 262 | super($crate::Slot::unsafe_new()) |
| 263 | .unsafe_construct($expr) |
| 264 | .unsafe_as_pin_unchecked() |
| 265 | }; |
| 266 | } |
| 267 | ``` |
| 268 | |
| 269 | Until `super_let` is stabilized, the outer `emplace!{}` works OK, it's just |
| 270 | unexpected and causes code formatting problems. |