Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,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 }
Expand Down
12 changes: 8 additions & 4 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1041,9 +1041,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 {
Expand Down Expand Up @@ -1138,7 +1140,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),
});
}
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_ty_utils/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down
22 changes: 15 additions & 7 deletions compiler/rustc_ty_utils/src/common_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
19 changes: 13 additions & 6 deletions library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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! {
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
8 changes: 6 additions & 2 deletions src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
32 changes: 30 additions & 2 deletions src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,36 @@ fn mutate(x: &UnsafePinned<i32>) {
unsafe { ptr.write(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 x = UnsafePinned::new(0);
let mut x = UnsafePinned::new(0i32);
mutate(&x);
assert_eq!(x.into_inner(), 42);
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) };
}
3 changes: 3 additions & 0 deletions tests/auxiliary/minicore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
18 changes: 14 additions & 4 deletions tests/codegen-llvm/function-arguments.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
4 changes: 3 additions & 1 deletion tests/rustdoc-html/empty-section.rs
Original file line number Diff line number Diff line change
@@ -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 {}
3 changes: 2 additions & 1 deletion tests/rustdoc-html/synthetic_auto/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
3 changes: 2 additions & 1 deletion tests/rustdoc-html/synthetic_auto/manual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
Loading