blob: 2e8efd4f19ee975b88a757f0325429d37ca0c609 [file] [log] [blame] [view]
Devin Jeanpierreb347dd32024-11-22 12:55:55 -08001# Unstable Features
2
3## Unstable features used by Crubit {#accepted}
4
5For 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 Jeanpierre181070f2024-11-22 13:47:41 -080011* **Use case:** What do we use the feature for?
Devin Jeanpierreb347dd32024-11-22 12:55:55 -080012* **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 Jeanpierreb347dd32024-11-22 12:55:55 -080033### `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 Georgiev2b50e122025-03-13 07:54:41 -0700104### `cfg_accessible`, `stmt_expr_attributes`, `proc_macro_hygiene`
Devin Jeanpierre831def32025-02-04 06:26:32 -0800105
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 Georgiev2b50e122025-03-13 07:54:41 -0700110 `stmt_expr_attributes`, `proc_macro_hygiene` lets us handle
111 backwards-incompatible changes affecting individual expressions.
Devin Jeanpierre831def32025-02-04 06:26:32 -0800112* **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 Georgiev2b50e122025-03-13 07:54:41 -0700116 that is currently used, and deleting the other one). Same for the rest.
Devin Jeanpierre831def32025-02-04 06:26:32 -0800117
Devin Jeanpierred4460092025-06-10 23:28:41 -0700118### `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 Jeanpierreb347dd32024-11-22 12:55:55 -0800130## Unstable features **not** used by Crubit {#rejected}
131
132The following features are ones we'd hypothetically like to use, but do not.
133
Devin Jeanpierre181070f2024-11-22 13:47:41 -0800134### `try_trait_v2`
Devin Jeanpierreb347dd32024-11-22 12:55:55 -0800135
136Crubit does aim to support the Abseil error type, `absl::Status`, which ideally
137would be usable everywhere a `Result` is for ergonomics. For example, we'd
138expect you to be able to write code like the following:
139
140```rust
141pub fn read() -> Status {
142 foo()?;
143}
144```
145
146Where `foo` returns a `Status`, or even an `io::Result`.
147
148For this to work with `Status` being *exactly the same type* as it is in C++,
149with the same layout, we need to use `try_trait_v2`.
150
151Until it is stabilized, or the cost/benefit becomes worthwhile, we can work
152around it by using `Result`, and converting when a `Result` is passed or
153returned by value.
154
Devin Jeanpierre52792272025-01-30 14:52:59 -0800155### `fn_traits`
156
157Much like `try_traits_v2`, we'd like to support C++ "function objects" which
158implement the call operator. In Rust, without `fn_traits`, the API is
159awkward: given a `foo` of type `std::function`, `absl::AnyInvocable`, or
160`absl::FunctionRef`, you cannot just write `foo()` to call it, because it cannot
161implement the `Fn*` family of traits. This makes these types less convenient to
162use than they are in C++, requiring something like `foo.as_closure()()` to
163create a closure out of a `FunctionRef` or similar.
164
165(Another use case we've examined in the past: in addition to implementing
166function objects, `fn_traits` could also be used to implement overloading,
167including overloaded function objects. For example, an overloaded function
168could, instead, be a constant with many different implementations of `Fn`, for
169different parameter types. Though, this ends up looking a bit
170odd: `(mystruct.overloaded)()`)
171
Devin Jeanpierre181070f2024-11-22 13:47:41 -0800172### `min_specialization`
Devin Jeanpierreb347dd32024-11-22 12:55:55 -0800173
174We have many use cases for `min_specialization`, including bindings for C++
175templates that have explicit template specialization or partial template
176specialization. However, they are not yet pressing, and specialization related
177Rust features are especially risky.
Devin Jeanpierre7fcc3402025-02-20 18:11:34 -0800178
179### `inherent_associated_types`
180
181Inherent associated types give a natural Rust spelling of the following C++
182type definition:
183
184```c++
185struct Foo {
186 using MyAlias = int;
187};
188```
Devin Jeanpierre29860b32025-03-25 16:57:45 -0700189
190### `disjoint_associated_types`
191
192[`disjoint_associated_types`](https://github.com/rust-lang/rfcs/pull/1672) was
193closed, but not necessarily permanently. See also
194https://github.com/rust-lang/rust/issues/20400.
195
196Not every C++ type supports the Rust move operation. For more on this, see
197[Classes and Structs](../cpp/classes_and_structs#trivially_relocatable).
198
199Crubit (in `experimental` mode) supports passing and returning these non-Rust-movable C++
200objects by value. But since they are not Rust-movable, they cannot literally
201be returned in Rust by value: `pub fn foo() -> X` performs a Rust move of its
202return type by definition.
203
204Instead, 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
207results 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
214The problem comes in with operator overloading: the following is valid:
215
216```rust
217impl Add<X> for &C {...}
218impl Add<Y> for &C {...}
219```
220But the equivalent using this lazy-construction trait is not:
221
222```rust
223impl<T: Ctor<Output=X>> Add<T> for &C {...}
224impl<T: Ctor<Output=Y>> Add<T> for &C {...}
225```
226
227Rust doesn't know that these two are disjoint, meaning that we cannot use the
228`Ctor` trait approach for traits.
229
230An alternative fix would be language support for in-place pinned construction.
231That would render the `Ctor` trait obsolete, and reduce Crubit's needs around
232trait coherence (as well as `negative_impls`, above).
Taylor Cramer7e125e82025-03-31 11:06:55 -0700233
234
235### `register_tool`
236
237We previously used this attribute to allow Crubit to read annotations on
238types/functions. However, the need for end-users to enable a feature gate and
239re-declare the tool caused us to migrate away to using `#[doc]` attributes.
240
241We use custom attributes so that Crubit can round-trip a type correctly, or to
242implement automated bridging so that a C++ `Status` becomes a Rust `Result<(),
Devin Jeanpierre3e1c00e2025-04-23 17:29:48 -0700243StatusError>`, or what have you.
244
245### `super_let`
246
247Crubit's `ctor.rs` crate uses a syntax like `emplace!{let x = ...}` to in-place
248initialize a pinned value using a C++ constructor. It cannot use the same trick
249that `pin!()` does, because it needs to actually call a function to initialize
250the memory, and so lifetime extension would not apply: the memory, being passed
251to 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
254macro expanding to a use of `super` to lifetime-extend the storage
255subexpression.
256
257For example:
258
259```rust
260macro_rules! emplace {
261 ($expr:expr) => {
262 super($crate::Slot::unsafe_new())
263 .unsafe_construct($expr)
264 .unsafe_as_pin_unchecked()
265 };
266}
267```
268
269Until `super_let` is stabilized, the outer `emplace!{}` works OK, it's just
270unexpected and causes code formatting problems.