Skip to content

Stacked Borrows: raw pointers inherit the tag from their parent pointer #142170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

RalfJung
Copy link
Member

@RalfJung RalfJung commented Jun 7, 2025

The biggest design mistake of SB in my opinion was the decision to have raw pointers be distinct from the reference they are derived from. This has multiple undesirable consequences:

  • Within a function, a let mut x and &raw mut x are not allowed to be used in an interleaving way, which is quite surprising and requires dedicated work-arounds e.g. in c2rust.
  • Casting a &mut T to a *const T results in a read-only pointer, which regularly startles people.
  • On the implementation side, this means we have to recognize all places where "safe" pointers turn into raw pointers, which turns out to be tricky for Box.

TB fully mitigates this by having a raw pointer inherit the tag from its parent pointer. I think we should do the same for SB. However, this requires a bit of hackery to prevent code like this from becoming UB:

fn foo(x: &mut [i32]) {
  let ptr = x.as_mut_ptr();
  let len = x.len();
  assert!(len > 0);
  ptr.write(0);
}

Under SB, x.len() causes a read of the entire slice which invalidates ptr since that is derived from a child of x (created as part of the implicit retag when calling as_mut_ptr).

So this experiment adds some special magic hackery to len to avoid x.len() from retagging anything. len is already special in invisible ways by having the function call be replaced by a MIR op; this extends that magic for the purposes of our alias tracking.

We also do one further change to the SB access logic: writing to a Unique does not invalidate SRW above this item. This, too, matches Tree Borrows. It does not affect local reasoning since owners of a Unique know whether any SRW have been pushed on top. SRW are now only created for UnsafeCell, so this allows aliasing of an &UnsafeCell with the &mut UnsafeCell from which it was derived (making it equivalent to a *mut UnsafeCell derived from that mutable reference).

This is definitely not the final answer, but it is a minimal step that lets us make SB slightly cleaner, thus unlocking some patterns in the ecosystem that Miri has so far rejected, and some cleanup in the compiler. Longer-term plans include a new attribute one can put on functions to avoid retags -- putting that attribute on len or as_mut_ptr would fix the above example.

The main potential downside of this PR is that it will lead Miri to accept code like let x = 5; (&raw const x).cast_mut().write(0); by default. This is discussed in rust-lang/unsafe-code-guidelines#400. Depending on how likely we are to make this UB in the future, we may want Miri to keep people away from those patterns. However, rejecting such code in an operational way is non-trivial; the only proposal we have is arguably a hack. Also, note that destructors will receive an &mut self pointing to such an immutable local variable, so operationally, it seems a bit futile to pretend that they are immutable.

WIP: This also gets rid of the "quirk" in Stacked Borrows, where reading from some pointer disabled the Unique above that pointer, but did not touch SRW/SRO derived from that Unique pointer. This quirk was always quite unfortunate since it violates the stack discipline and can be used to write code that is accepted by SB but rejected by TB. The changes described above make the quirk a lot less effective since it no longer applies to raw pointers (as those no longer get a separate item in the stack); rather than having a quirk that only applies to particular situations involving UnsafeCell, IMO we should get rid of it entirely.

TODO:

  • Check which further TB tests should be moved to "both borrows" now.
  • Entirely remove RetagKind::Raw.
  • Add mir-opt tests checking the MIR around x.len() looks the way we want it to.
  • If we can indeed get rid of the quirk: remove leftovers of "disable Unique" infrastructure (including removing Permission::Disabled).

try-job: *gnu*aux

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jun 7, 2025
@RalfJung
Copy link
Member Author

RalfJung commented Jun 7, 2025

@bors2 try

@rust-bors
Copy link

rust-bors bot commented Jun 7, 2025

⌛ Trying commit 65c6154 with merge 3942c14

To cancel the try build, run the command @bors2 try cancel.

rust-bors bot added a commit that referenced this pull request Jun 7, 2025
Stacked Borrows: raw pointers inherit the tag from their parent pointer

The biggest design mistake of SB in my opinion was the decision to have raw pointers be distinct from the reference they are derived from. This has multiple undesirable consequences:
- Within a function, a `let mut x` and `&raw mut x` are not allowed to be used in an interleaving way, which is quite surprising and requires dedicated work-arounds e.g. in c2rust.
- Casting a `&mut T` to a `*const T` results in a read-only pointer, which regularly startles people.
- On the implementation side, this means we have to recognize all places where "safe" pointers turn into raw pointers, which turns out to be tricky for `Box`.

TB fully mitigates this by having a raw pointer inherit the tag from its parent pointer. I think we should do the same for SB. However, this requires a bit of hackery to prevent code like this from becoming UB:
```rust
fn foo(x: &mut [i32]) {
  let ptr = x.as_mut_ptr();
  let len = x.len();
  assert!(len > 0);
  ptr.write(0);
}
```
Under SB, `x.len()` causes a read of the entire slice which invalidates `ptr` since that is derived from a child of `x` (created as part of the implicit retag when calling `as_mut_ptr`).

So this experiment adds some special magic hackery to `len` to avoid `x.len()` from retagging anything. `len` is already special in invisible ways by having the function call be replaced by a MIR op; this extends that magic for the purposes of our alias tracking.

This is definitely not the final answer, but it is a minimal step that lets us make SB slightly cleaner, thus unlocking some patterns in the ecosystem that Miri has so far rejected, and some cleanup in the compiler. Longer-term plans include a new attribute one can put on functions to avoid retags -- putting that attribute on `len` *or* `as_mut_ptr` would fix the above example.

TODO:
- Check which further TB tests should be moved to "both borrows" now.
- Entirely remove `RetagKind::Raw`.
- Add mir-opt tests checking the MIR around `x.len()` looks the way we want it to.

try-job: `*gnu*aux`
@rust-log-analyzer

This comment has been minimized.

@bjorn3
Copy link
Member

bjorn3 commented Jun 7, 2025

So this experiment adds some special magic hackery to len to avoid x.len() from retagging anything. len is already special in invisible ways by having the function call be replaced by a MIR op; this extends that magic for the purposes of our alias tracking.

It seems to me like a cleaner way to handle this would be to add an attribute that can be used to indicate that a given function argument should not be retagged (and thus also should lose noalias on the LLVM side). Kinda like how we already have #[may_dangle] for drop impls (as unstable attribute).

@RalfJung RalfJung force-pushed the sb-raw-retag branch 2 times, most recently from ba46876 to 7816394 Compare June 7, 2025 17:19
@RalfJung
Copy link
Member Author

RalfJung commented Jun 7, 2025

It seems to me like a cleaner way to handle this would be to add an attribute that can be used to indicate that a given function argument should not be retagged (and thus also should lose noalias on the LLVM side). Kinda like how we already have #[may_dangle] for drop impls (as unstable attribute).

That's the longer-term solution I mentioned. I don't know how to implement the caller-side behavior of this, since the retags come from the reborrows that are inserted during MIR building. (The callee-side retags of function arguments are easy to suppress, comparatively.)

Another form this attribute could take is that we could have functions that take a raw pointer receiver, and we arrange things so that they get called even if the receiver is actually a reference, implicitly coercing that to a raw pointer. That would neatly prevent the implicit reborrows during MIR building (I hope), but it needs cooperation of method resolution which I really don't want to touch.^^ This restricts the attribute to functions that can safely work on raw pointers, but that does not seem entirely unreasonable -- it definitely applies to our main candidates (as_mut_ptr, as_ptr, len).

@rust-bors
Copy link

rust-bors bot commented Jun 7, 2025

💔 Test failed

@RalfJung
Copy link
Member Author

RalfJung commented Jun 7, 2025

That failure was interesting. Previously,we happened to accept code that does something like this on an interior mutable local variable

  • &mut local as *mut _, store this pointer
  • (&local).get()
  • use the previously stored pointer

This used to work thanks to the "quirk" where invalidating a mutable reference with a "foreign read" in SB does not necessarily invalidate all its derived raw pointers. With this PR, that no longer works since the raw pointer is identical to the mutable reference, so the code needs to be adjusted a little. Arguably this code was highly sketchy before...
(In TB, this worked fine since the variable is never written to.)

@bors2 try

@rust-bors
Copy link

rust-bors bot commented Jun 7, 2025

⌛ Trying commit 8ae194b with merge afbfda1

To cancel the try build, run the command @bors2 try cancel.

rust-bors bot added a commit that referenced this pull request Jun 7, 2025
Stacked Borrows: raw pointers inherit the tag from their parent pointer

The biggest design mistake of SB in my opinion was the decision to have raw pointers be distinct from the reference they are derived from. This has multiple undesirable consequences:
- Within a function, a `let mut x` and `&raw mut x` are not allowed to be used in an interleaving way, which is quite surprising and requires dedicated work-arounds e.g. in c2rust.
- Casting a `&mut T` to a `*const T` results in a read-only pointer, which regularly startles people.
- On the implementation side, this means we have to recognize all places where "safe" pointers turn into raw pointers, which turns out to be tricky for `Box`.

TB fully mitigates this by having a raw pointer inherit the tag from its parent pointer. I think we should do the same for SB. However, this requires a bit of hackery to prevent code like this from becoming UB:
```rust
fn foo(x: &mut [i32]) {
  let ptr = x.as_mut_ptr();
  let len = x.len();
  assert!(len > 0);
  ptr.write(0);
}
```
Under SB, `x.len()` causes a read of the entire slice which invalidates `ptr` since that is derived from a child of `x` (created as part of the implicit retag when calling `as_mut_ptr`).

So this experiment adds some special magic hackery to `len` to avoid `x.len()` from retagging anything. `len` is already special in invisible ways by having the function call be replaced by a MIR op; this extends that magic for the purposes of our alias tracking.

We also do one further change to the SB access logic: writing to a `Unique` does not invalidate SRW above this item. This, too, matches Tree Borrows. It does not affect local reasoning since owners of a `Unique` know whether any SRW have been pushed on top.

This is definitely not the final answer, but it is a minimal step that lets us make SB slightly cleaner, thus unlocking some patterns in the ecosystem that Miri has so far rejected, and some cleanup in the compiler. Longer-term plans include a new attribute one can put on functions to avoid retags -- putting that attribute on `len` *or* `as_mut_ptr` would fix the above example.

The main potential downside of this PR is that it will lead Miri to accept code like `let x = 5; (&raw const x).cast_mut().write(0);` by default. This is discussed in rust-lang/unsafe-code-guidelines#400. Depending on how likely we are to make this UB in the future, we may want Miri to keep people away from those patterns. However, rejecting such code in an operational way is non-trivial; the only proposal we have is [arguably a hack](rust-lang/unsafe-code-guidelines#400 (comment)). Also, note that destructors will receive an `&mut self` pointing to such an immutable local variable, so operationally, it seems a bit futile to pretend that they are immutable.

TODO:
- Check which further TB tests should be moved to "both borrows" now.
- Entirely remove `RetagKind::Raw`.
- Add mir-opt tests checking the MIR around `x.len()` looks the way we want it to.

try-job: `*gnu*aux`
@rust-bors
Copy link

rust-bors bot commented Jun 7, 2025

☀️ Try build successful (CI)
Build commit: afbfda1 (afbfda1c386d2ff220c491e3e3d479a6dbb18002)

@RalfJung
Copy link
Member Author

RalfJung commented Jun 8, 2025

Amazingly, it seems like we can even entirely get rid of the "quirk" with this approach. This feels like SB is finally getting into the form it was meant to have. :D

@bors2 try

rust-bors bot added a commit that referenced this pull request Jun 8, 2025
Stacked Borrows: raw pointers inherit the tag from their parent pointer

The biggest design mistake of SB in my opinion was the decision to have raw pointers be distinct from the reference they are derived from. This has multiple undesirable consequences:
- Within a function, a `let mut x` and `&raw mut x` are not allowed to be used in an interleaving way, which is quite surprising and requires dedicated work-arounds e.g. in c2rust.
- Casting a `&mut T` to a `*const T` results in a read-only pointer, which regularly startles people.
- On the implementation side, this means we have to recognize all places where "safe" pointers turn into raw pointers, which turns out to be tricky for `Box`.

TB fully mitigates this by having a raw pointer inherit the tag from its parent pointer. I think we should do the same for SB. However, this requires a bit of hackery to prevent code like this from becoming UB:
```rust
fn foo(x: &mut [i32]) {
  let ptr = x.as_mut_ptr();
  let len = x.len();
  assert!(len > 0);
  ptr.write(0);
}
```
Under SB, `x.len()` causes a read of the entire slice which invalidates `ptr` since that is derived from a child of `x` (created as part of the implicit retag when calling `as_mut_ptr`).

So this experiment adds some special magic hackery to `len` to avoid `x.len()` from retagging anything. `len` is already special in invisible ways by having the function call be replaced by a MIR op; this extends that magic for the purposes of our alias tracking.

We also do one further change to the SB access logic: writing to a `Unique` does not invalidate SRW above this item. This, too, matches Tree Borrows. It does not affect local reasoning since owners of a `Unique` know whether any SRW have been pushed on top.

This is definitely not the final answer, but it is a minimal step that lets us make SB slightly cleaner, thus unlocking some patterns in the ecosystem that Miri has so far rejected, and some cleanup in the compiler. Longer-term plans include a new attribute one can put on functions to avoid retags -- putting that attribute on `len` *or* `as_mut_ptr` would fix the above example.

The main potential downside of this PR is that it will lead Miri to accept code like `let x = 5; (&raw const x).cast_mut().write(0);` by default. This is discussed in rust-lang/unsafe-code-guidelines#400. Depending on how likely we are to make this UB in the future, we may want Miri to keep people away from those patterns. However, rejecting such code in an operational way is non-trivial; the only proposal we have is [arguably a hack](rust-lang/unsafe-code-guidelines#400 (comment)). Also, note that destructors will receive an `&mut self` pointing to such an immutable local variable, so operationally, it seems a bit futile to pretend that they are immutable.

TODO:
- Check which further TB tests should be moved to "both borrows" now.
- Entirely remove `RetagKind::Raw`.
- Add mir-opt tests checking the MIR around `x.len()` looks the way we want it to.

try-job: `*gnu*aux`
@rust-bors
Copy link

rust-bors bot commented Jun 8, 2025

⌛ Trying commit add928f with merge a500351

To cancel the try build, run the command @bors2 try cancel.

@RalfJung
Copy link
Member Author

RalfJung commented Jun 8, 2025

@saethlin how hard would it be to use your infrastructure to compare the Miri from this branch with the Miri we currently ship on some of the most widely used crates, to see if there's any surprises there?

@rust-bors
Copy link

rust-bors bot commented Jun 8, 2025

☀️ Try build successful (CI)
Build commit: a500351 (a5003515d6973c94bb4d726609112ce20b05c32d)

@Mark-Simulacrum
Copy link
Member

@bors2 try jobs=dist-x86_64-linux

@rust-bors
Copy link

rust-bors bot commented Jun 8, 2025

⌛ Trying commit add928f with merge 54bf046

To cancel the try build, run the command @bors2 try cancel.

rust-bors bot added a commit that referenced this pull request Jun 8, 2025
Stacked Borrows: raw pointers inherit the tag from their parent pointer

The biggest design mistake of SB in my opinion was the decision to have raw pointers be distinct from the reference they are derived from. This has multiple undesirable consequences:
- Within a function, a `let mut x` and `&raw mut x` are not allowed to be used in an interleaving way, which is quite surprising and requires dedicated work-arounds e.g. in c2rust.
- Casting a `&mut T` to a `*const T` results in a read-only pointer, which regularly startles people.
- On the implementation side, this means we have to recognize all places where "safe" pointers turn into raw pointers, which turns out to be tricky for `Box`.

TB fully mitigates this by having a raw pointer inherit the tag from its parent pointer. I think we should do the same for SB. However, this requires a bit of hackery to prevent code like this from becoming UB:
```rust
fn foo(x: &mut [i32]) {
  let ptr = x.as_mut_ptr();
  let len = x.len();
  assert!(len > 0);
  ptr.write(0);
}
```
Under SB, `x.len()` causes a read of the entire slice which invalidates `ptr` since that is derived from a child of `x` (created as part of the implicit retag when calling `as_mut_ptr`).

So this experiment adds some special magic hackery to `len` to avoid `x.len()` from retagging anything. `len` is already special in invisible ways by having the function call be replaced by a MIR op; this extends that magic for the purposes of our alias tracking.

We also do one further change to the SB access logic: writing to a `Unique` does not invalidate SRW above this item. This, too, matches Tree Borrows. It does not affect local reasoning since owners of a `Unique` know whether any SRW have been pushed on top. SRW are now only created for `UnsafeCell`, so this allows aliasing of an `&UnsafeCell` with the `&mut UnsafeCell` from which it was derived (making it equivalent to a `*mut UnsafeCell` derived from that mutable reference).

This is definitely not the final answer, but it is a minimal step that lets us make SB slightly cleaner, thus unlocking some patterns in the ecosystem that Miri has so far rejected, and some cleanup in the compiler. Longer-term plans include a new attribute one can put on functions to avoid retags -- putting that attribute on `len` *or* `as_mut_ptr` would fix the above example.

The main potential downside of this PR is that it will lead Miri to accept code like `let x = 5; (&raw const x).cast_mut().write(0);` by default. This is discussed in rust-lang/unsafe-code-guidelines#400. Depending on how likely we are to make this UB in the future, we may want Miri to keep people away from those patterns. However, rejecting such code in an operational way is non-trivial; the only proposal we have is [arguably a hack](rust-lang/unsafe-code-guidelines#400 (comment)). Also, note that destructors will receive an `&mut self` pointing to such an immutable local variable, so operationally, it seems a bit futile to pretend that they are immutable.

WIP: This also gets rid of the "quirk" in Stacked Borrows, where reading from some pointer disabled the `Unique` above that pointer, but did *not* touch SRW/SRO derived from that `Unique` pointer. This quirk was always quite unfortunate since it violates the stack discipline and can be used to write code that is accepted by SB but rejected by TB. The changes described above make the quirk a lot less effective since it no longer applies to raw pointers (as those no longer get a separate item in the stack); rather than having a quirk that only applies to particular situations involving `UnsafeCell`, IMO we should get rid of it entirely.

TODO:
- Check which further TB tests should be moved to "both borrows" now.
- Entirely remove `RetagKind::Raw`.
- Add mir-opt tests checking the MIR around `x.len()` looks the way we want it to.
- If we can indeed get rid of the quirk: remove leftovers of "disable `Unique`" infrastructure (including removing `Permission::Disabled`).

try-job: `*gnu*aux`
try-job: `dist-x86_64-linux`
@rust-bors
Copy link

rust-bors bot commented Jun 8, 2025

☀️ Try build successful (CI)
Build commit: 54bf046 (54bf04651a59570ff6b8c7e080a3fa9931ed05dc)

@saethlin
Copy link
Member

saethlin commented Jun 8, 2025

@saethlin how hard would it be to use your infrastructure to compare the Miri from this branch with the Miri we currently ship on some of the most widely used crates, to see if there's any surprises there?

I'm doing the top 1000 crates now.

@saethlin
Copy link
Member

saethlin commented Jun 8, 2025

This does fix some crates, but it breaks more. This jumps out at me from arrayvec:

test test_arraystring_const_constructible ... error: Undefined Behavior: attempting a write access using <296827> at alloc124998[0x4], but that tag does not exist in the borrow stack for this location
   --> /tmp/arrayvec-0.7.6/src/array_string.rs:291:13
    |
291 |             ptr::copy_nonoverlapping(src, dst, s.len());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |             |
    |             attempting a write access using <296827> at alloc124998[0x4], but that tag does not exist in the borrow stack for this location
    |             this error occurs as part of an access at alloc124998[0x4..0x9]
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <296827> was created by a Unique retag at offsets [0x4..0xe]
   --> /tmp/arrayvec-0.7.6/src/array_string.rs:427:9
    |
427 |         self.xs.as_mut_ptr() as *mut u8
    |         ^^^^^^^^^^^^^^^^^^^^
help: <296827> was later invalidated at offsets [0x0..0x10] by a SharedReadOnly retag
   --> /tmp/arrayvec-0.7.6/src/array_string.rs:289:45
    |
289 |             let dst = self.as_mut_ptr().add(self.len());
    |                                             ^^^^

Here are your short lists of crates that were fixed:

aes/0.9.0-pre.3
criterion/0.5.1
ctr/0.10.0-pre.2
generator/0.8.4
hashbrown/0.15.3
nalgebra/0.33.2
petgraph/0.8.1
pprof/0.14.0
smartstring/1.0.1
zune-jpeg/0.5.0-rc2

And crates that were broken:

arrayvec/0.7.6
aws-runtime/1.5.7
aws-sigv4/1.3.2
aws-smithy-eventstream/0.61.0
aws-smithy-http/0.62.1
base64ct/1.8.0
blake3/1.8.2
bumpalo/3.18.1
bytes/1.10.1
compact_str/0.9.0
crossbeam-channel/0.5.15
csv-core/0.1.12
flume/0.11.1
generator/0.8.5
h2/0.4.10
http/1.3.1
lexical-core/1.0.5
num-format/0.4.4
portable-atomic-util/0.2.4
rust_decimal/1.37.1
scc/2.3.4
schemars/1.0.0-alpha.20
smallvec/2.0.0-alpha.11
tiff/0.9.1
xxhash-rust/0.8.15
zune-jpeg/0.5.0-rc4

It's likely that some of these are duplicates, because they were fixed or broken through a dependency.

@RalfJung
Copy link
Member Author

RalfJung commented Jun 9, 2025

Thanks! Breaking arrayvec and smallvec is not great... do you have the error message for smallvec as well?

That arrayvec issue arises because self.len here is ArrayString::len, so the magic for slice::len does not help.

Very odd that hasbrown appears under "fixed", that repo has Miri under CI so it should already work before this PR.

@saethlin
Copy link
Member

saethlin commented Jun 9, 2025

My tool enables all features that are enabled in package.metadata.docs.rs or package.metadata.playground. Hashbrown's Miri tests only enable --features=nightly but its docs enable --features=nightly,rayon,serde,raw-entry. It's the rayon test that is problematic, because it uses crossbeam-epoch.

The smallvec error is:

test tests::collect_from_iter ... error: Undefined Behavior: attempting a write access using <192457> at alloc82031[0x8], but that tag does not exist in the borrow stack for this location
    --> src/lib.rs:1523:21
     |
1523 |                     ptr.add(guard.len).write(item);
     |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |                     |
     |                     attempting a write access using <192457> at alloc82031[0x8], but that tag does not exist in the borrow stack for this location
     |                     this error occurs as part of an access at alloc82031[0x8..0x9]
     |
     = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
     = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <192457> was created by a Unique retag at offsets [0x8..0x18]
    --> src/lib.rs:181:26
     |
181  |     fn as_mut_ptr_inline(&mut self) -> *mut T {
     |                          ^^^^^^^^^
help: <192457> was later invalidated at offsets [0x0..0x18] by a SharedReadOnly retag
    --> src/lib.rs:1518:31
     |
1518 |                 let mut len = self.len();
     |                               ^^^^

@RalfJung
Copy link
Member Author

RalfJung commented Jun 9, 2025

Okay so the smallvec erorr is basically the same as the arrayvec error, another len function (this time SmallVec::len).

Bummer, I think that kills the approach of special-casing slice::len. Seems like we do indeed need some form of attribute that slice, arrayvec, smallvec all set on their as_(mut_)ptr methods to prevent any reborrows. For SmallVec is extra tricky as it needs to look at len to determine where to get that raw pointer from, so using a raw pointer receiver would not be sound. We truly do need "this is a reference but neither caller nor callee should do any retags".

@RalfJung
Copy link
Member Author

RalfJung commented Jun 9, 2025

I see no other way to adjust SB to move it closer to TB, so I think my path forward here will be to find ways to make TB stricter to move it closer to SB, until the remaining gap is uncontroversial and we can drop SB.

I opened rust-lang/unsafe-code-guidelines#573 to track that. Thanks all for the help and feedback here!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants