Skip to content

Commit ee603cd

Browse files
committed
UnsafePinned: implement opsem effects of UnsafeUnpin
1 parent 53b6f89 commit ee603cd

10 files changed

Lines changed: 108 additions & 29 deletions

File tree

compiler/rustc_middle/src/query/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,6 +1711,10 @@ rustc_queries! {
17111711
query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
17121712
desc { "computing whether `{}` is freeze", env.value }
17131713
}
1714+
/// Query backing `Ty::is_unsafe_unpin`.
1715+
query is_unsafe_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
1716+
desc { "computing whether `{}` is `UnsafeUnpin`", env.value }
1717+
}
17141718
/// Query backing `Ty::is_unpin`.
17151719
query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
17161720
desc { "computing whether `{}` is `Unpin`", env.value }

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,9 +1041,11 @@ where
10411041
hir::Mutability::Not => {
10421042
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
10431043
}
1044-
hir::Mutability::Mut => {
1045-
PointerKind::MutableRef { unpin: optimize && ty.is_unpin(tcx, typing_env) }
1046-
}
1044+
hir::Mutability::Mut => PointerKind::MutableRef {
1045+
unpin: optimize
1046+
&& ty.is_unpin(tcx, typing_env)
1047+
&& ty.is_unsafe_unpin(tcx, typing_env),
1048+
},
10471049
};
10481050

10491051
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
@@ -1138,7 +1140,9 @@ where
11381140
debug_assert!(pointee.safe.is_none());
11391141
let optimize = tcx.sess.opts.optimize != OptLevel::No;
11401142
pointee.safe = Some(PointerKind::Box {
1141-
unpin: optimize && boxed_ty.is_unpin(tcx, typing_env),
1143+
unpin: optimize
1144+
&& boxed_ty.is_unpin(tcx, typing_env)
1145+
&& boxed_ty.is_unsafe_unpin(tcx, typing_env),
11421146
global: this.ty.is_box_global(tcx),
11431147
});
11441148
}

compiler/rustc_middle/src/ty/util.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,14 +1194,23 @@ impl<'tcx> Ty<'tcx> {
11941194
}
11951195
}
11961196

1197+
/// Checks whether values of this type `T` implement the `UnsafeUnpin` trait.
1198+
pub fn is_unsafe_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
1199+
self.is_trivially_unpin() || tcx.is_unsafe_unpin_raw(typing_env.as_query_input(self))
1200+
}
1201+
11971202
/// Checks whether values of this type `T` implement the `Unpin` trait.
1203+
///
1204+
/// Note that this is a safe trait, so it cannot be very semantically meaningful.
1205+
/// However, as a hack to mitigate <https://github.com/rust-lang/rust/issues/63818> until a
1206+
/// proper solution is implemented, we do give special semantics to the `Unpin` trait.
11981207
pub fn is_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
11991208
self.is_trivially_unpin() || tcx.is_unpin_raw(typing_env.as_query_input(self))
12001209
}
12011210

1202-
/// Fast path helper for testing if a type is `Unpin`.
1211+
/// Fast path helper for testing if a type is `Unpin` *and* `UnsafeUnpin`.
12031212
///
1204-
/// Returning true means the type is known to be `Unpin`. Returning
1213+
/// Returning true means the type is known to be `Unpin` and `UnsafeUnpin`. Returning
12051214
/// `false` means nothing -- could be `Unpin`, might not be.
12061215
fn is_trivially_unpin(self) -> bool {
12071216
match self.kind() {

compiler/rustc_ty_utils/src/abi.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,12 @@ fn arg_attrs_for_rust_scalar<'tcx>(
306306
let kind = if let Some(kind) = pointee.safe {
307307
Some(kind)
308308
} else if let Some(pointee) = drop_target_pointee {
309+
assert_eq!(pointee, layout.ty.builtin_deref(true).unwrap());
310+
assert_eq!(offset, Size::ZERO);
309311
// The argument to `drop_in_place` is semantically equivalent to a mutable reference.
310-
Some(PointerKind::MutableRef { unpin: pointee.is_unpin(tcx, cx.typing_env) })
312+
let mutref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, pointee);
313+
let layout = cx.layout_of(mutref).unwrap();
314+
layout.pointee_info_at(&cx, offset).and_then(|pi| pi.safe)
311315
} else {
312316
None
313317
};

compiler/rustc_ty_utils/src/common_traits.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,43 @@ use rustc_span::DUMMY_SP;
88
use rustc_trait_selection::traits;
99

1010
fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
11-
is_item_raw(tcx, query, LangItem::Copy)
11+
is_trait_raw(tcx, query, LangItem::Copy)
1212
}
1313

1414
fn is_use_cloned_raw<'tcx>(
1515
tcx: TyCtxt<'tcx>,
1616
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
1717
) -> bool {
18-
is_item_raw(tcx, query, LangItem::UseCloned)
18+
is_trait_raw(tcx, query, LangItem::UseCloned)
1919
}
2020

2121
fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
22-
is_item_raw(tcx, query, LangItem::Sized)
22+
is_trait_raw(tcx, query, LangItem::Sized)
2323
}
2424

2525
fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
26-
is_item_raw(tcx, query, LangItem::Freeze)
26+
is_trait_raw(tcx, query, LangItem::Freeze)
27+
}
28+
29+
fn is_unsafe_unpin_raw<'tcx>(
30+
tcx: TyCtxt<'tcx>,
31+
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
32+
) -> bool {
33+
is_trait_raw(tcx, query, LangItem::UnsafeUnpin)
2734
}
2835

2936
fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
30-
is_item_raw(tcx, query, LangItem::Unpin)
37+
is_trait_raw(tcx, query, LangItem::Unpin)
3138
}
3239

3340
fn is_async_drop_raw<'tcx>(
3441
tcx: TyCtxt<'tcx>,
3542
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
3643
) -> bool {
37-
is_item_raw(tcx, query, LangItem::AsyncDrop)
44+
is_trait_raw(tcx, query, LangItem::AsyncDrop)
3845
}
3946

40-
fn is_item_raw<'tcx>(
47+
fn is_trait_raw<'tcx>(
4148
tcx: TyCtxt<'tcx>,
4249
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
4350
item: LangItem,
@@ -53,6 +60,7 @@ pub(crate) fn provide(providers: &mut Providers) {
5360
is_use_cloned_raw,
5461
is_sized_raw,
5562
is_freeze_raw,
63+
is_unsafe_unpin_raw,
5664
is_unpin_raw,
5765
is_async_drop_raw,
5866
..*providers

library/core/src/marker.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -932,14 +932,20 @@ marker_impls! {
932932
/// This is part of [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html), and is
933933
/// tracked by [#125735](https://github.com/rust-lang/rust/issues/125735).
934934
#[lang = "unsafe_unpin"]
935-
pub(crate) unsafe auto trait UnsafeUnpin {}
935+
#[unstable(feature = "unsafe_unpin", issue = "125735")]
936+
pub unsafe auto trait UnsafeUnpin {}
936937

938+
#[unstable(feature = "unsafe_unpin", issue = "125735")]
937939
impl<T: ?Sized> !UnsafeUnpin for UnsafePinned<T> {}
938-
unsafe impl<T: ?Sized> UnsafeUnpin for PhantomData<T> {}
939-
unsafe impl<T: ?Sized> UnsafeUnpin for *const T {}
940-
unsafe impl<T: ?Sized> UnsafeUnpin for *mut T {}
941-
unsafe impl<T: ?Sized> UnsafeUnpin for &T {}
942-
unsafe impl<T: ?Sized> UnsafeUnpin for &mut T {}
940+
marker_impls! {
941+
#[unstable(feature = "unsafe_unpin", issue = "125735")]
942+
unsafe UnsafeUnpin for
943+
{T: ?Sized} PhantomData<T>,
944+
{T: ?Sized} *const T,
945+
{T: ?Sized} *mut T,
946+
{T: ?Sized} &T,
947+
{T: ?Sized} &mut T,
948+
}
943949

944950
/// Types that do not require any pinning guarantees.
945951
///
@@ -1032,6 +1038,7 @@ impl !Unpin for PhantomPinned {}
10321038
// continue working. Ideally PhantomPinned could just wrap an `UnsafePinned<()>` to get the same
10331039
// effect, but we can't add a new field to an already stable unit struct -- that would be a breaking
10341040
// change.
1041+
#[unstable(feature = "unsafe_unpin", issue = "125735")]
10351042
impl !UnsafeUnpin for PhantomPinned {}
10361043

10371044
marker_impls! {

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ impl NewPermission {
7171
access: None,
7272
protector: None,
7373
}
74-
} else if pointee.is_unpin(*cx.tcx, cx.typing_env()) {
74+
} else if pointee.is_unpin(*cx.tcx, cx.typing_env())
75+
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env())
76+
{
7577
// A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`.
7678
NewPermission::Uniform {
7779
perm: Permission::Unique,
@@ -129,7 +131,9 @@ impl NewPermission {
129131
fn from_box_ty<'tcx>(ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>) -> Self {
130132
// `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling).
131133
let pointee = ty.builtin_deref(true).unwrap();
132-
if pointee.is_unpin(*cx.tcx, cx.typing_env()) {
134+
if pointee.is_unpin(*cx.tcx, cx.typing_env())
135+
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env())
136+
{
133137
// A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only
134138
// a weak protector).
135139
NewPermission::Uniform {

src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ impl<'tcx> NewPermission {
133133
retag_kind: RetagKind,
134134
cx: &crate::MiriInterpCx<'tcx>,
135135
) -> Option<Self> {
136-
let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env());
136+
let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env())
137+
&& pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env());
137138
let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
138139
let is_protected = retag_kind == RetagKind::FnEntry;
139140

src/tools/miri/tests/pass/both_borrows/unsafe_pinned.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,36 @@ fn mutate(x: &UnsafePinned<i32>) {
99
unsafe { ptr.write(42) };
1010
}
1111

12+
fn mut_alias(x: &mut UnsafePinned<i32>, y: &mut UnsafePinned<i32>) {
13+
unsafe {
14+
x.get().write(0);
15+
y.get().write(0);
16+
x.get().write(0);
17+
y.get().write(0);
18+
}
19+
}
20+
21+
// Also try this with a type for which we implement `Unpin`, just to be extra mean.
22+
struct MyUnsafePinned<T>(UnsafePinned<T>);
23+
impl<T> Unpin for MyUnsafePinned<T> {}
24+
25+
fn my_mut_alias(x: &mut MyUnsafePinned<i32>, y: &mut MyUnsafePinned<i32>) {
26+
unsafe {
27+
x.0.get().write(0);
28+
y.0.get().write(0);
29+
x.0.get().write(0);
30+
y.0.get().write(0);
31+
}
32+
}
33+
1234
fn main() {
13-
let x = UnsafePinned::new(0);
35+
let mut x = UnsafePinned::new(0i32);
1436
mutate(&x);
15-
assert_eq!(x.into_inner(), 42);
37+
assert_eq!(unsafe { x.get().read() }, 42);
38+
39+
let ptr = &raw mut x;
40+
unsafe { mut_alias(&mut *ptr, &mut *ptr) };
41+
42+
let ptr = ptr.cast::<MyUnsafePinned<i32>>();
43+
unsafe { my_mut_alias(&mut *ptr, &mut *ptr) };
1644
}

tests/codegen-llvm/function-arguments.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes
22
#![crate_type = "lib"]
33
#![feature(rustc_attrs)]
4-
#![feature(allocator_api)]
4+
#![feature(allocator_api, unsafe_unpin)]
55

6-
use std::marker::PhantomPinned;
6+
use std::marker::{PhantomPinned, UnsafeUnpin};
77
use std::mem::MaybeUninit;
88
use std::num::NonZero;
99
use std::ptr::NonNull;
@@ -259,11 +259,21 @@ pub fn trait_raw(_: *const dyn Drop) {}
259259

260260
// CHECK: @trait_box(ptr noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
261261
#[no_mangle]
262-
pub fn trait_box(_: Box<dyn Drop + Unpin>) {}
262+
pub fn trait_box(_: Box<dyn Drop + Unpin + UnsafeUnpin>) {}
263+
264+
// Ensure that removing *either* `Unpin` or `UnsafeUnpin` is enough to lose the attribute.
265+
// CHECK: @trait_box_pin1(ptr noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
266+
#[no_mangle]
267+
pub fn trait_box_pin1(_: Box<dyn Drop + Unpin>) {}
268+
// CHECK: @trait_box_pin2(ptr noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
269+
#[no_mangle]
270+
pub fn trait_box_pin2(_: Box<dyn Drop + UnsafeUnpin>) {}
263271

264272
// CHECK: { ptr, ptr } @trait_option(ptr noalias noundef align 1 %x.0, ptr %x.1)
265273
#[no_mangle]
266-
pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + Unpin>> {
274+
pub fn trait_option(
275+
x: Option<Box<dyn Drop + Unpin + UnsafeUnpin>>,
276+
) -> Option<Box<dyn Drop + Unpin + UnsafeUnpin>> {
267277
x
268278
}
269279

0 commit comments

Comments
 (0)