Rollup merge of #151365 - RalfJung:unsafe-unpin-opsem, r=BoxyUwU
UnsafePinned: implement opsem effects of UnsafeUnpin This implements the next step for https://github.com/rust-lang/rust/issues/125735: actually making `UnsafePinned` have special opsem effects by suppressing the `noalias` *even if* the type is wrapped in an `Unpin` wrapper. For backwards compatibility we also still keep the `Unpin` hack, i.e. a type must be both `Unpin` and `UnsafeUnpin` to get `noalias`.
This commit is contained in:
commit
33c2a6eba9
16 changed files with 123 additions and 36 deletions
|
|
@ -1693,6 +1693,10 @@ rustc_queries! {
|
|||
query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is freeze", env.value }
|
||||
}
|
||||
/// Query backing `Ty::is_unsafe_unpin`.
|
||||
query is_unsafe_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is `UnsafeUnpin`", env.value }
|
||||
}
|
||||
/// Query backing `Ty::is_unpin`.
|
||||
query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
desc { "computing whether `{}` is `Unpin`", env.value }
|
||||
|
|
|
|||
|
|
@ -1046,9 +1046,11 @@ where
|
|||
hir::Mutability::Not => {
|
||||
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
|
||||
}
|
||||
hir::Mutability::Mut => {
|
||||
PointerKind::MutableRef { unpin: optimize && ty.is_unpin(tcx, typing_env) }
|
||||
}
|
||||
hir::Mutability::Mut => PointerKind::MutableRef {
|
||||
unpin: optimize
|
||||
&& ty.is_unpin(tcx, typing_env)
|
||||
&& ty.is_unsafe_unpin(tcx, typing_env),
|
||||
},
|
||||
};
|
||||
|
||||
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
|
||||
|
|
@ -1143,7 +1145,9 @@ where
|
|||
debug_assert!(pointee.safe.is_none());
|
||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||
pointee.safe = Some(PointerKind::Box {
|
||||
unpin: optimize && boxed_ty.is_unpin(tcx, typing_env),
|
||||
unpin: optimize
|
||||
&& boxed_ty.is_unpin(tcx, typing_env)
|
||||
&& boxed_ty.is_unsafe_unpin(tcx, typing_env),
|
||||
global: this.ty.is_box_global(tcx),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1189,14 +1189,23 @@ impl<'tcx> Ty<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implement the `UnsafeUnpin` trait.
|
||||
pub fn is_unsafe_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
|
||||
self.is_trivially_unpin() || tcx.is_unsafe_unpin_raw(typing_env.as_query_input(self))
|
||||
}
|
||||
|
||||
/// Checks whether values of this type `T` implement the `Unpin` trait.
|
||||
///
|
||||
/// Note that this is a safe trait, so it cannot be very semantically meaningful.
|
||||
/// However, as a hack to mitigate <https://github.com/rust-lang/rust/issues/63818> until a
|
||||
/// proper solution is implemented, we do give special semantics to the `Unpin` trait.
|
||||
pub fn is_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
|
||||
self.is_trivially_unpin() || tcx.is_unpin_raw(typing_env.as_query_input(self))
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type is `Unpin`.
|
||||
/// Fast path helper for testing if a type is `Unpin` *and* `UnsafeUnpin`.
|
||||
///
|
||||
/// Returning true means the type is known to be `Unpin`. Returning
|
||||
/// Returning true means the type is known to be `Unpin` and `UnsafeUnpin`. Returning
|
||||
/// `false` means nothing -- could be `Unpin`, might not be.
|
||||
fn is_trivially_unpin(self) -> bool {
|
||||
match self.kind() {
|
||||
|
|
|
|||
|
|
@ -306,8 +306,12 @@ fn arg_attrs_for_rust_scalar<'tcx>(
|
|||
let kind = if let Some(kind) = pointee.safe {
|
||||
Some(kind)
|
||||
} else if let Some(pointee) = drop_target_pointee {
|
||||
assert_eq!(pointee, layout.ty.builtin_deref(true).unwrap());
|
||||
assert_eq!(offset, Size::ZERO);
|
||||
// The argument to `drop_in_place` is semantically equivalent to a mutable reference.
|
||||
Some(PointerKind::MutableRef { unpin: pointee.is_unpin(tcx, cx.typing_env) })
|
||||
let mutref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, pointee);
|
||||
let layout = cx.layout_of(mutref).unwrap();
|
||||
layout.pointee_info_at(&cx, offset).and_then(|pi| pi.safe)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,36 +8,43 @@ use rustc_span::DUMMY_SP;
|
|||
use rustc_trait_selection::traits;
|
||||
|
||||
fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
is_item_raw(tcx, query, LangItem::Copy)
|
||||
is_trait_raw(tcx, query, LangItem::Copy)
|
||||
}
|
||||
|
||||
fn is_use_cloned_raw<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
|
||||
) -> bool {
|
||||
is_item_raw(tcx, query, LangItem::UseCloned)
|
||||
is_trait_raw(tcx, query, LangItem::UseCloned)
|
||||
}
|
||||
|
||||
fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
is_item_raw(tcx, query, LangItem::Sized)
|
||||
is_trait_raw(tcx, query, LangItem::Sized)
|
||||
}
|
||||
|
||||
fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
is_item_raw(tcx, query, LangItem::Freeze)
|
||||
is_trait_raw(tcx, query, LangItem::Freeze)
|
||||
}
|
||||
|
||||
fn is_unsafe_unpin_raw<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
|
||||
) -> bool {
|
||||
is_trait_raw(tcx, query, LangItem::UnsafeUnpin)
|
||||
}
|
||||
|
||||
fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
|
||||
is_item_raw(tcx, query, LangItem::Unpin)
|
||||
is_trait_raw(tcx, query, LangItem::Unpin)
|
||||
}
|
||||
|
||||
fn is_async_drop_raw<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
|
||||
) -> bool {
|
||||
is_item_raw(tcx, query, LangItem::AsyncDrop)
|
||||
is_trait_raw(tcx, query, LangItem::AsyncDrop)
|
||||
}
|
||||
|
||||
fn is_item_raw<'tcx>(
|
||||
fn is_trait_raw<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
|
||||
item: LangItem,
|
||||
|
|
@ -53,6 +60,7 @@ pub(crate) fn provide(providers: &mut Providers) {
|
|||
is_use_cloned_raw,
|
||||
is_sized_raw,
|
||||
is_freeze_raw,
|
||||
is_unsafe_unpin_raw,
|
||||
is_unpin_raw,
|
||||
is_async_drop_raw,
|
||||
..*providers
|
||||
|
|
|
|||
|
|
@ -927,14 +927,20 @@ marker_impls! {
|
|||
/// This is part of [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html), and is
|
||||
/// tracked by [#125735](https://github.com/rust-lang/rust/issues/125735).
|
||||
#[lang = "unsafe_unpin"]
|
||||
pub(crate) unsafe auto trait UnsafeUnpin {}
|
||||
#[unstable(feature = "unsafe_unpin", issue = "125735")]
|
||||
pub unsafe auto trait UnsafeUnpin {}
|
||||
|
||||
#[unstable(feature = "unsafe_unpin", issue = "125735")]
|
||||
impl<T: ?Sized> !UnsafeUnpin for UnsafePinned<T> {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for PhantomData<T> {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for *const T {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for *mut T {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for &T {}
|
||||
unsafe impl<T: ?Sized> UnsafeUnpin for &mut T {}
|
||||
marker_impls! {
|
||||
#[unstable(feature = "unsafe_unpin", issue = "125735")]
|
||||
unsafe UnsafeUnpin for
|
||||
{T: ?Sized} PhantomData<T>,
|
||||
{T: ?Sized} *const T,
|
||||
{T: ?Sized} *mut T,
|
||||
{T: ?Sized} &T,
|
||||
{T: ?Sized} &mut T,
|
||||
}
|
||||
|
||||
/// Types that do not require any pinning guarantees.
|
||||
///
|
||||
|
|
@ -1027,6 +1033,7 @@ impl !Unpin for PhantomPinned {}
|
|||
// continue working. Ideally PhantomPinned could just wrap an `UnsafePinned<()>` to get the same
|
||||
// effect, but we can't add a new field to an already stable unit struct -- that would be a breaking
|
||||
// change.
|
||||
#[unstable(feature = "unsafe_unpin", issue = "125735")]
|
||||
impl !UnsafeUnpin for PhantomPinned {}
|
||||
|
||||
marker_impls! {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ use crate::{fmt, intrinsics, ptr, ub_checks};
|
|||
issue = "none"
|
||||
)]
|
||||
pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed {
|
||||
#[doc(hidden)]
|
||||
/// A type like `Self` but with a niche that includes zero.
|
||||
type NonZeroInner: Sized + Copy;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,9 @@ impl NewPermission {
|
|||
access: None,
|
||||
protector: None,
|
||||
}
|
||||
} else if pointee.is_unpin(*cx.tcx, cx.typing_env()) {
|
||||
} else if pointee.is_unpin(*cx.tcx, cx.typing_env())
|
||||
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env())
|
||||
{
|
||||
// A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`.
|
||||
NewPermission::Uniform {
|
||||
perm: Permission::Unique,
|
||||
|
|
@ -129,7 +131,9 @@ impl NewPermission {
|
|||
fn from_box_ty<'tcx>(ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>) -> Self {
|
||||
// `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling).
|
||||
let pointee = ty.builtin_deref(true).unwrap();
|
||||
if pointee.is_unpin(*cx.tcx, cx.typing_env()) {
|
||||
if pointee.is_unpin(*cx.tcx, cx.typing_env())
|
||||
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env())
|
||||
{
|
||||
// A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only
|
||||
// a weak protector).
|
||||
NewPermission::Uniform {
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ impl<'tcx> NewPermission {
|
|||
retag_kind: RetagKind,
|
||||
cx: &crate::MiriInterpCx<'tcx>,
|
||||
) -> Option<Self> {
|
||||
let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env());
|
||||
let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env())
|
||||
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env());
|
||||
let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
|
||||
let is_protected = retag_kind == RetagKind::FnEntry;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,36 @@ fn mutate(x: &UnsafePinned<i32>) {
|
|||
unsafe { ptr.write(42) };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = UnsafePinned::new(0);
|
||||
mutate(&x);
|
||||
assert_eq!(x.into_inner(), 42);
|
||||
fn mut_alias(x: &mut UnsafePinned<i32>, y: &mut UnsafePinned<i32>) {
|
||||
unsafe {
|
||||
x.get().write(0);
|
||||
y.get().write(0);
|
||||
x.get().write(0);
|
||||
y.get().write(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Also try this with a type for which we implement `Unpin`, just to be extra mean.
|
||||
struct MyUnsafePinned<T>(UnsafePinned<T>);
|
||||
impl<T> Unpin for MyUnsafePinned<T> {}
|
||||
|
||||
fn my_mut_alias(x: &mut MyUnsafePinned<i32>, y: &mut MyUnsafePinned<i32>) {
|
||||
unsafe {
|
||||
x.0.get().write(0);
|
||||
y.0.get().write(0);
|
||||
x.0.get().write(0);
|
||||
y.0.get().write(0);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut x = UnsafePinned::new(0i32);
|
||||
mutate(&x);
|
||||
assert_eq!(unsafe { x.get().read() }, 42);
|
||||
|
||||
let ptr = &raw mut x;
|
||||
unsafe { mut_alias(&mut *ptr, &mut *ptr) };
|
||||
|
||||
let ptr = ptr.cast::<MyUnsafePinned<i32>>();
|
||||
unsafe { my_mut_alias(&mut *ptr, &mut *ptr) };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@ pub trait BikeshedGuaranteedNoDrop {}
|
|||
#[lang = "freeze"]
|
||||
pub unsafe auto trait Freeze {}
|
||||
|
||||
#[lang = "unsafe_unpin"]
|
||||
pub unsafe auto trait UnsafeUnpin {}
|
||||
|
||||
#[lang = "unpin"]
|
||||
#[diagnostic::on_unimplemented(
|
||||
note = "consider using the `pin!` macro\nconsider using `Box::pin` if you need to access the pinned value outside of the current scope",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes
|
||||
#![crate_type = "lib"]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(allocator_api, unsafe_unpin)]
|
||||
|
||||
use std::marker::PhantomPinned;
|
||||
use std::marker::{PhantomPinned, UnsafeUnpin};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::num::NonZero;
|
||||
use std::ptr::NonNull;
|
||||
|
|
@ -259,11 +259,21 @@ pub fn trait_raw(_: *const dyn Drop) {}
|
|||
|
||||
// CHECK: @trait_box(ptr noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
|
||||
#[no_mangle]
|
||||
pub fn trait_box(_: Box<dyn Drop + Unpin>) {}
|
||||
pub fn trait_box(_: Box<dyn Drop + Unpin + UnsafeUnpin>) {}
|
||||
|
||||
// Ensure that removing *either* `Unpin` or `UnsafeUnpin` is enough to lose the attribute.
|
||||
// CHECK: @trait_box_pin1(ptr noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
|
||||
#[no_mangle]
|
||||
pub fn trait_box_pin1(_: Box<dyn Drop + Unpin>) {}
|
||||
// CHECK: @trait_box_pin2(ptr noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
|
||||
#[no_mangle]
|
||||
pub fn trait_box_pin2(_: Box<dyn Drop + UnsafeUnpin>) {}
|
||||
|
||||
// CHECK: { ptr, ptr } @trait_option(ptr noalias noundef align 1 %x.0, ptr %x.1)
|
||||
#[no_mangle]
|
||||
pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + Unpin>> {
|
||||
pub fn trait_option(
|
||||
x: Option<Box<dyn Drop + Unpin + UnsafeUnpin>>,
|
||||
) -> Option<Box<dyn Drop + Unpin + UnsafeUnpin>> {
|
||||
x
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ where
|
|||
//@ has - '//h3[@class="code-header"]' 'impl<B> Send for Switch<B>where <B as Signal>::Item: Send'
|
||||
//@ has - '//h3[@class="code-header"]' 'impl<B> Sync for Switch<B>where <B as Signal>::Item: Sync'
|
||||
//@ count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
|
||||
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 6
|
||||
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 7
|
||||
// The number here will need updating when new auto traits are added: ^
|
||||
pub struct Switch<B: Signal> {
|
||||
pub inner: <B as Signal2>::Item2,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
#![crate_name = "foo"]
|
||||
#![feature(negative_impls, freeze_impls, freeze)]
|
||||
#![feature(negative_impls, freeze_impls, freeze, unsafe_unpin)]
|
||||
|
||||
pub struct Foo;
|
||||
|
||||
//@ has foo/struct.Foo.html
|
||||
//@ !hasraw - 'Auto Trait Implementations'
|
||||
// Manually un-implement all auto traits for Foo:
|
||||
impl !Send for Foo {}
|
||||
impl !Sync for Foo {}
|
||||
impl !std::marker::Freeze for Foo {}
|
||||
impl !std::marker::UnsafeUnpin for Foo {}
|
||||
impl !std::marker::Unpin for Foo {}
|
||||
impl !std::panic::RefUnwindSafe for Foo {}
|
||||
impl !std::panic::UnwindSafe for Foo {}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
//@ has - '//h3[@class="code-header"]' 'impl<T> Send for Foo<T>where T: Send'
|
||||
//@ has - '//h3[@class="code-header"]' 'impl<T> Sync for Foo<T>where T: Sync'
|
||||
//@ count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
|
||||
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 6
|
||||
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 7
|
||||
// The number here will need updating when new auto traits are added: ^
|
||||
pub struct Foo<T> {
|
||||
field: T,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
// 'impl<T> Send for Foo<T>'
|
||||
//
|
||||
//@ count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
|
||||
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5
|
||||
//@ count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 6
|
||||
// The number here will need updating when new auto traits are added: ^
|
||||
pub struct Foo<T> {
|
||||
field: T,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue