Skip to content

Commit aa6c5d4

Browse files
committed
Initial implementation of UnsafePinned and UnsafeUnpin
1 parent ced8e65 commit aa6c5d4

File tree

31 files changed

+339
-78
lines changed

31 files changed

+339
-78
lines changed

compiler/rustc_abi/src/layout.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
240240
repr: &ReprOptions,
241241
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
242242
is_enum: bool,
243-
is_unsafe_cell: bool,
243+
is_special_no_niche: bool,
244244
scalar_valid_range: (Bound<u128>, Bound<u128>),
245245
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
246246
discriminants: impl Iterator<Item = (VariantIdx, i128)>,
@@ -273,7 +273,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
273273
repr,
274274
variants,
275275
is_enum,
276-
is_unsafe_cell,
276+
is_special_no_niche,
277277
scalar_valid_range,
278278
always_sized,
279279
present_first,
@@ -418,7 +418,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
418418
repr: &ReprOptions,
419419
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
420420
is_enum: bool,
421-
is_unsafe_cell: bool,
421+
is_special_no_niche: bool,
422422
scalar_valid_range: (Bound<u128>, Bound<u128>),
423423
always_sized: bool,
424424
present_first: VariantIdx,
@@ -437,7 +437,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
437437
let mut st = self.univariant(&variants[v], repr, kind)?;
438438
st.variants = Variants::Single { index: v };
439439

440-
if is_unsafe_cell {
440+
if is_special_no_niche {
441441
let hide_niches = |scalar: &mut _| match scalar {
442442
Scalar::Initialized { value, valid_range } => {
443443
*valid_range = WrappingRange::full(value.size(dl))

compiler/rustc_abi/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,8 @@ where
18281828
pub enum PointerKind {
18291829
/// Shared reference. `frozen` indicates the absence of any `UnsafeCell`.
18301830
SharedRef { frozen: bool },
1831-
/// Mutable reference. `unpin` indicates the absence of any pinned data.
1831+
/// Mutable reference. `unpin` indicates the absence of any pinned
1832+
/// data (`UnsafePinned` or `PhantomPinned`).
18321833
MutableRef { unpin: bool },
18331834
/// Box. `unpin` indicates the absence of any pinned data. `global` indicates whether this box
18341835
/// uses the global allocator or a custom one.

compiler/rustc_ast_ir/src/lib.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ use rustc_macros::{Decodable, Encodable, HashStable_NoContext};
1111
pub mod visit;
1212

1313
/// The movability of a coroutine / closure literal:
14-
/// whether a coroutine contains self-references, causing it to be `!Unpin`.
14+
/// whether a coroutine contains self-references, causing it to be
15+
/// `!Unpin + !UnsafeUnpin`.
1516
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
1617
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
1718
pub enum Movability {
18-
/// May contain self-references, `!Unpin`.
19+
/// May contain self-references, `!Unpin + !UnsafeUnpin`.
1920
Static,
20-
/// Must not contain self-references, `Unpin`.
21+
/// Must not contain self-references, `!Unpin + !UnsafeUnpin`.
2122
Movable,
2223
}
2324

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3407,7 +3407,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
34073407
Some(3)
34083408
} else if string.starts_with("static") {
34093409
// `static` is 6 chars long
3410-
// This is used for `!Unpin` coroutines
3410+
// This is used for immovable (self-referential) coroutines
34113411
Some(6)
34123412
} else {
34133413
None

compiler/rustc_codegen_ssa/src/traits/type_.rs

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ pub trait DerivedTypeCodegenMethods<'tcx>:
8585
ty.is_freeze(self.tcx(), self.typing_env())
8686
}
8787

88+
fn type_is_unsafe_unpin(&self, ty: Ty<'tcx>) -> bool {
89+
ty.is_unsafe_unpin(self.tcx(), self.typing_env())
90+
}
91+
8892
fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool {
8993
if ty.is_sized(self.tcx(), self.typing_env()) {
9094
return false;

compiler/rustc_const_eval/src/interpret/eval_context.rs

+5
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
251251
ty.is_freeze(*self.tcx, self.typing_env)
252252
}
253253

254+
#[inline]
255+
pub fn type_is_unsafe_unpin(&self, ty: Ty<'tcx>) -> bool {
256+
ty.is_unsafe_unpin(*self.tcx, self.typing_env)
257+
}
258+
254259
pub fn load_mir(
255260
&self,
256261
instance: ty::InstanceKind<'tcx>,

compiler/rustc_hir/src/lang_items.rs

+3
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ language_item_table! {
181181
DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
182182

183183
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
184+
UnsafeUnpin, sym::unsafe_unpin, unsafe_unpin_trait, Target::Trait, GenericRequirement::Exact(0);
184185

185186
FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
186187
FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
@@ -234,6 +235,8 @@ language_item_table! {
234235
IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
235236

236237
UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
238+
UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None;
239+
237240
VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
238241

239242
Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);

compiler/rustc_lint/src/types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -867,8 +867,8 @@ fn ty_is_known_nonnull<'tcx>(
867867
return true;
868868
}
869869

870-
// `UnsafeCell` has its niche hidden.
871-
if def.is_unsafe_cell() {
870+
// `UnsafeCell` and `UnsafePinned` have their niche hidden.
871+
if def.is_unsafe_cell() || def.is_unsafe_pinned() {
872872
return false;
873873
}
874874

compiler/rustc_middle/src/query/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1527,9 +1527,9 @@ rustc_queries! {
15271527
query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
15281528
desc { "computing whether `{}` is freeze", env.value }
15291529
}
1530-
/// Query backing `Ty::is_unpin`.
1531-
query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
1532-
desc { "computing whether `{}` is `Unpin`", env.value }
1530+
/// Query backing `Ty::is_unsafe_unpin`.
1531+
query is_unsafe_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
1532+
desc { "computing whether `{}` is `UnsafeUnpin`", env.value }
15331533
}
15341534
/// Query backing `Ty::needs_drop`.
15351535
query needs_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {

compiler/rustc_middle/src/ty/adt.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ bitflags::bitflags! {
5454
const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8;
5555
/// Indicates whether the type is `UnsafeCell`.
5656
const IS_UNSAFE_CELL = 1 << 9;
57+
/// Indicates whether the type is `UnsafePinned`.
58+
const IS_UNSAFE_PINNED = 1 << 10;
5759
/// Indicates whether the type is anonymous.
58-
const IS_ANONYMOUS = 1 << 10;
60+
const IS_ANONYMOUS = 1 << 11;
5961
}
6062
}
6163
rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -301,6 +303,9 @@ impl AdtDefData {
301303
if tcx.is_lang_item(did, LangItem::UnsafeCell) {
302304
flags |= AdtFlags::IS_UNSAFE_CELL;
303305
}
306+
if tcx.is_lang_item(did, LangItem::UnsafePinned) {
307+
flags |= AdtFlags::IS_UNSAFE_PINNED;
308+
}
304309

305310
AdtDefData { did, variants, flags, repr }
306311
}
@@ -393,6 +398,12 @@ impl<'tcx> AdtDef<'tcx> {
393398
self.flags().contains(AdtFlags::IS_UNSAFE_CELL)
394399
}
395400

401+
/// Returns `true` if this is `UnsafePinned<T>`.
402+
#[inline]
403+
pub fn is_unsafe_pinned(self) -> bool {
404+
self.flags().contains(AdtFlags::IS_UNSAFE_PINNED)
405+
}
406+
396407
/// Returns `true` if this is `ManuallyDrop<T>`.
397408
#[inline]
398409
pub fn is_manually_drop(self) -> bool {

compiler/rustc_middle/src/ty/context.rs

+1
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ bidirectional_lang_item_map! {
717717
TransmuteTrait,
718718
Tuple,
719719
Unpin,
720+
UnsafeUnpin,
720721
Unsize,
721722
// tidy-alphabetical-end
722723
}

compiler/rustc_middle/src/ty/layout.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1022,16 +1022,16 @@ where
10221022
}
10231023
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
10241024
// Use conservative pointer kind if not optimizing. This saves us the
1025-
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
1025+
// Freeze/UnsafeUnpin queries, and can save time in the codegen backend (noalias
10261026
// attributes in LLVM have compile-time cost even in unoptimized builds).
10271027
let optimize = tcx.sess.opts.optimize != OptLevel::No;
10281028
let kind = match mt {
10291029
hir::Mutability::Not => {
10301030
PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
10311031
}
1032-
hir::Mutability::Mut => {
1033-
PointerKind::MutableRef { unpin: optimize && ty.is_unpin(tcx, typing_env) }
1034-
}
1032+
hir::Mutability::Mut => PointerKind::MutableRef {
1033+
unpin: optimize && ty.is_unsafe_unpin(tcx, typing_env),
1034+
},
10351035
};
10361036

10371037
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
@@ -1124,7 +1124,7 @@ where
11241124
debug_assert!(pointee.safe.is_none());
11251125
let optimize = tcx.sess.opts.optimize != OptLevel::No;
11261126
pointee.safe = Some(PointerKind::Box {
1127-
unpin: optimize && boxed_ty.is_unpin(tcx, typing_env),
1127+
unpin: optimize && boxed_ty.is_unsafe_unpin(tcx, typing_env),
11281128
global: this.ty.is_box_global(tcx),
11291129
});
11301130
}

compiler/rustc_middle/src/ty/util.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ impl<'tcx> Ty<'tcx> {
11931193

11941194
/// Fast path helper for testing if a type is `Freeze`.
11951195
///
1196-
/// Returning true means the type is known to be `Freeze`. Returning
1196+
/// Returning `true` means the type is known to be `Freeze`. Returning
11971197
/// `false` means nothing -- could be `Freeze`, might not be.
11981198
pub fn is_trivially_freeze(self) -> bool {
11991199
match self.kind() {
@@ -1227,16 +1227,18 @@ impl<'tcx> Ty<'tcx> {
12271227
}
12281228
}
12291229

1230-
/// Checks whether values of this type `T` implement the `Unpin` trait.
1231-
pub fn is_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
1232-
self.is_trivially_unpin() || tcx.is_unpin_raw(typing_env.as_query_input(self))
1230+
/// Checks whether values of this type `T` implement the `UnsafeUnpin` trait.
1231+
///
1232+
/// For more information, see [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html).
1233+
pub fn is_unsafe_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
1234+
self.is_trivially_unsafe_unpin() || tcx.is_unsafe_unpin_raw(typing_env.as_query_input(self))
12331235
}
12341236

1235-
/// Fast path helper for testing if a type is `Unpin`.
1237+
/// Fast path helper for testing if a type is `UnsafeUnpin`.
12361238
///
1237-
/// Returning true means the type is known to be `Unpin`. Returning
1238-
/// `false` means nothing -- could be `Unpin`, might not be.
1239-
fn is_trivially_unpin(self) -> bool {
1239+
/// Returning `true` means the type is known to be `UnsafeUnpin`. Returning
1240+
/// `false` means nothing -- could be `UnsafeUnpin`, might not be.
1241+
fn is_trivially_unsafe_unpin(self) -> bool {
12401242
match self.kind() {
12411243
ty::Int(_)
12421244
| ty::Uint(_)
@@ -1250,8 +1252,8 @@ impl<'tcx> Ty<'tcx> {
12501252
| ty::FnDef(..)
12511253
| ty::Error(_)
12521254
| ty::FnPtr(..) => true,
1253-
ty::Tuple(fields) => fields.iter().all(Self::is_trivially_unpin),
1254-
ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_unpin(),
1255+
ty::Tuple(fields) => fields.iter().all(Self::is_trivially_unsafe_unpin),
1256+
ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_unsafe_unpin(),
12551257
ty::Adt(..)
12561258
| ty::Bound(..)
12571259
| ty::Closure(..)

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1060,11 +1060,15 @@ where
10601060

10611061
ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"),
10621062

1063-
// Coroutines have one special built-in candidate, `Unpin`, which
1064-
// takes precedence over the structural auto trait candidate being
1065-
// assembled.
1063+
// Coroutines have two special built-in candidates, `UnsafeUnpin`
1064+
// and `Unpin`, which take precedence over the structural auto trait
1065+
// candidate being assembled.
10661066
ty::Coroutine(def_id, _)
1067-
if self.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) =>
1067+
if self.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin)
1068+
|| self.cx().is_lang_item(
1069+
goal.predicate.def_id(),
1070+
TraitSolverLangItem::UnsafeUnpin,
1071+
) =>
10681072
{
10691073
match self.cx().coroutine_movability(def_id) {
10701074
Movability::Static => Some(Err(NoSolution)),

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2152,6 +2152,8 @@ symbols! {
21522152
unsafe_fields,
21532153
unsafe_no_drop_flag,
21542154
unsafe_pin_internals,
2155+
unsafe_pinned,
2156+
unsafe_unpin,
21552157
unsize,
21562158
unsized_const_param_ty,
21572159
unsized_const_params,

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -727,16 +727,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
727727
candidates.ambiguous = true;
728728
}
729729
ty::Coroutine(coroutine_def_id, _)
730-
if self.tcx().is_lang_item(def_id, LangItem::Unpin) =>
730+
if self.tcx().is_lang_item(def_id, LangItem::Unpin)
731+
|| self.tcx().is_lang_item(def_id, LangItem::UnsafeUnpin) =>
731732
{
732733
match self.tcx().coroutine_movability(coroutine_def_id) {
733734
hir::Movability::Static => {
734-
// Immovable coroutines are never `Unpin`, so
735-
// suppress the normal auto-impl candidate for it.
735+
// Immovable coroutines are never `[Unsafe]Unpin`,
736+
// so suppress the normal auto-impl candidate for it.
736737
}
737738
hir::Movability::Movable => {
738-
// Movable coroutines are always `Unpin`, so add an
739-
// unconditional builtin candidate.
739+
// Movable coroutines are always `[Unsafe]Unpin`,
740+
// so add an unconditional builtin candidate.
740741
candidates.vec.push(BuiltinCandidate { has_nested: false });
741742
}
742743
}

compiler/rustc_ty_utils/src/abi.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -379,19 +379,21 @@ fn adjust_for_rust_scalar<'tcx>(
379379
Some(kind)
380380
} else if let Some(pointee) = drop_target_pointee {
381381
// The argument to `drop_in_place` is semantically equivalent to a mutable reference.
382-
Some(PointerKind::MutableRef { unpin: pointee.is_unpin(tcx, cx.typing_env) })
382+
Some(PointerKind::MutableRef { unpin: pointee.is_unsafe_unpin(tcx, cx.typing_env) })
383383
} else {
384384
None
385385
};
386386
if let Some(kind) = kind {
387387
attrs.pointee_align = Some(pointee.align);
388388

389389
// `Box` are not necessarily dereferenceable for the entire duration of the function as
390-
// they can be deallocated at any time. Same for non-frozen shared references (see
391-
// <https://github.com/rust-lang/rust/pull/98017>), and for mutable references to
392-
// potentially self-referential types (see
393-
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>). If LLVM had a way
394-
// to say "dereferenceable on entry" we could use it here.
390+
// they can be deallocated at any time. Same for `!Freeze` shared references
391+
// (see <https://github.com/rust-lang/rust/pull/98017>), and for mutable
392+
// references to `!UnsafeUnpin` (ie. potentially self-referential types)
393+
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>
394+
// and <https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html>).
395+
//
396+
// If LLVM had a way to say "dereferenceable on entry" we could use it here.
395397
attrs.pointee_size = match kind {
396398
PointerKind::Box { .. }
397399
| PointerKind::SharedRef { frozen: false }

compiler/rustc_ty_utils/src/common_traits.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx,
1818
is_item_raw(tcx, query, LangItem::Freeze)
1919
}
2020

21-
fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool {
22-
is_item_raw(tcx, query, LangItem::Unpin)
21+
fn is_unsafe_unpin_raw<'tcx>(
22+
tcx: TyCtxt<'tcx>,
23+
query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>,
24+
) -> bool {
25+
is_item_raw(tcx, query, LangItem::UnsafeUnpin)
2326
}
2427

2528
fn is_item_raw<'tcx>(
@@ -33,5 +36,6 @@ fn is_item_raw<'tcx>(
3336
}
3437

3538
pub(crate) fn provide(providers: &mut Providers) {
36-
*providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers };
39+
*providers =
40+
Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unsafe_unpin_raw, ..*providers };
3741
}

compiler/rustc_ty_utils/src/layout.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,9 @@ fn layout_of_uncached<'tcx>(
619619
));
620620
}
621621

622+
// UnsafeCell and UnsafePinned both disable niche optimizations
623+
let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned();
624+
622625
let get_discriminant_type =
623626
|min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max);
624627

@@ -647,7 +650,7 @@ fn layout_of_uncached<'tcx>(
647650
&def.repr(),
648651
&variants,
649652
def.is_enum(),
650-
def.is_unsafe_cell(),
653+
is_special_no_niche,
651654
tcx.layout_scalar_valid_range(def.did()),
652655
get_discriminant_type,
653656
discriminants_iter(),
@@ -673,7 +676,7 @@ fn layout_of_uncached<'tcx>(
673676
&def.repr(),
674677
&variants,
675678
def.is_enum(),
676-
def.is_unsafe_cell(),
679+
is_special_no_niche,
677680
tcx.layout_scalar_valid_range(def.did()),
678681
get_discriminant_type,
679682
discriminants_iter(),
@@ -1068,6 +1071,7 @@ fn coroutine_layout<'tcx>(
10681071
// would do the same for us here.
10691072
// See <https://github.com/rust-lang/rust/issues/63818>, <https://github.com/rust-lang/miri/issues/3780>.
10701073
// FIXME: Remove when <https://github.com/rust-lang/rust/issues/125735> is implemented and aliased coroutine fields are wrapped in `UnsafePinned`.
1074+
// FIXME(unsafe_pinned)
10711075
largest_niche: None,
10721076
size,
10731077
align,

0 commit comments

Comments
 (0)