diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index b8773f9ff38f6..b8a813e9e0ab9 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -240,7 +240,7 @@ impl LayoutCalculator { repr: &ReprOptions, variants: &IndexSlice>, is_enum: bool, - is_unsafe_cell: bool, + is_special_no_niche: bool, scalar_valid_range: (Bound, Bound), discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discriminants: impl Iterator, @@ -273,7 +273,7 @@ impl LayoutCalculator { repr, variants, is_enum, - is_unsafe_cell, + is_special_no_niche, scalar_valid_range, always_sized, present_first, @@ -418,7 +418,7 @@ impl LayoutCalculator { repr: &ReprOptions, variants: &IndexSlice>, is_enum: bool, - is_unsafe_cell: bool, + is_special_no_niche: bool, scalar_valid_range: (Bound, Bound), always_sized: bool, present_first: VariantIdx, @@ -437,7 +437,7 @@ impl LayoutCalculator { let mut st = self.univariant(&variants[v], repr, kind)?; st.variants = Variants::Single { index: v }; - if is_unsafe_cell { + if is_special_no_niche { let hide_niches = |scalar: &mut _| match scalar { Scalar::Initialized { value, valid_range } => { *valid_range = WrappingRange::full(value.size(dl)) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 259f1c18ea8e9..be1b6815d7245 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1828,7 +1828,8 @@ where pub enum PointerKind { /// Shared reference. `frozen` indicates the absence of any `UnsafeCell`. SharedRef { frozen: bool }, - /// Mutable reference. `unpin` indicates the absence of any pinned data. + /// Mutable reference. `unpin` indicates the absence of any pinned + /// data (`UnsafePinned` or `PhantomPinned`). MutableRef { unpin: bool }, /// Box. `unpin` indicates the absence of any pinned data. `global` indicates whether this box /// uses the global allocator or a custom one. diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index ff9d940ce9f28..57116d5068914 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -11,13 +11,14 @@ use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; pub mod visit; /// The movability of a coroutine / closure literal: -/// whether a coroutine contains self-references, causing it to be `!Unpin`. +/// whether a coroutine contains self-references, causing it to be +/// `!Unpin + !UnsafeUnpin`. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum Movability { - /// May contain self-references, `!Unpin`. + /// May contain self-references, `!Unpin + !UnsafeUnpin`. Static, - /// Must not contain self-references, `Unpin`. + /// Must not contain self-references, `!Unpin + !UnsafeUnpin`. Movable, } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 6269728e67f73..1c25083dc0c73 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3407,7 +3407,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { Some(3) } else if string.starts_with("static") { // `static` is 6 chars long - // This is used for `!Unpin` coroutines + // This is used for immovable (self-referential) coroutines Some(6) } else { None diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index c178ebc596e1e..5e022633864ad 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -85,6 +85,10 @@ pub trait DerivedTypeCodegenMethods<'tcx>: ty.is_freeze(self.tcx(), self.typing_env()) } + fn type_is_unsafe_unpin(&self, ty: Ty<'tcx>) -> bool { + ty.is_unsafe_unpin(self.tcx(), self.typing_env()) + } + fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { if ty.is_sized(self.tcx(), self.typing_env()) { return false; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 66a75113652f1..0c63e451b73aa 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -251,6 +251,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ty.is_freeze(*self.tcx, self.typing_env) } + #[inline] + pub fn type_is_unsafe_unpin(&self, ty: Ty<'tcx>) -> bool { + ty.is_unsafe_unpin(*self.tcx, self.typing_env) + } + pub fn load_mir( &self, instance: ty::InstanceKind<'tcx>, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 1852987b1677f..e0f55cfc61915 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -181,6 +181,7 @@ language_item_table! { DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None; Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0); + UnsafeUnpin, sym::unsafe_unpin, unsafe_unpin_trait, Target::Trait, GenericRequirement::Exact(0); FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0); FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; @@ -234,6 +235,8 @@ language_item_table! { IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; + UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None; + VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None; Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 68b1f435a4cf7..8a6d2ade72ceb 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -867,8 +867,8 @@ fn ty_is_known_nonnull<'tcx>( return true; } - // `UnsafeCell` has its niche hidden. - if def.is_unsafe_cell() { + // `UnsafeCell` and `UnsafePinned` have their niche hidden. + if def.is_unsafe_cell() || def.is_unsafe_pinned() { return false; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 478ac19d199bd..14b46c1f7f5ad 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1527,9 +1527,9 @@ rustc_queries! { query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is freeze", 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 } + /// 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::needs_drop`. query needs_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index e28fcc555dcd1..7ab328c71a225 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -54,8 +54,10 @@ bitflags::bitflags! { const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; /// Indicates whether the type is `UnsafeCell`. const IS_UNSAFE_CELL = 1 << 9; + /// Indicates whether the type is `UnsafePinned`. + const IS_UNSAFE_PINNED = 1 << 10; /// Indicates whether the type is anonymous. - const IS_ANONYMOUS = 1 << 10; + const IS_ANONYMOUS = 1 << 11; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } @@ -301,6 +303,9 @@ impl AdtDefData { if tcx.is_lang_item(did, LangItem::UnsafeCell) { flags |= AdtFlags::IS_UNSAFE_CELL; } + if tcx.is_lang_item(did, LangItem::UnsafePinned) { + flags |= AdtFlags::IS_UNSAFE_PINNED; + } AdtDefData { did, variants, flags, repr } } @@ -393,6 +398,12 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_UNSAFE_CELL) } + /// Returns `true` if this is `UnsafePinned`. + #[inline] + pub fn is_unsafe_pinned(self) -> bool { + self.flags().contains(AdtFlags::IS_UNSAFE_PINNED) + } + /// Returns `true` if this is `ManuallyDrop`. #[inline] pub fn is_manually_drop(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a9b4593c13da6..4da335560bf6c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -717,6 +717,7 @@ bidirectional_lang_item_map! { TransmuteTrait, Tuple, Unpin, + UnsafeUnpin, Unsize, // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 4b3e29b7c6cc8..908f8b40fe3bd 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1022,16 +1022,16 @@ where } ty::Ref(_, ty, mt) if offset.bytes() == 0 => { // Use conservative pointer kind if not optimizing. This saves us the - // Freeze/Unpin queries, and can save time in the codegen backend (noalias + // Freeze/UnsafeUnpin queries, and can save time in the codegen backend (noalias // attributes in LLVM have compile-time cost even in unoptimized builds). let optimize = tcx.sess.opts.optimize != OptLevel::No; let kind = match mt { 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_unsafe_unpin(tcx, typing_env), + }, }; tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo { @@ -1124,7 +1124,7 @@ 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_unsafe_unpin(tcx, typing_env), global: this.ty.is_box_global(tcx), }); } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 94bd359f6eb0f..f6a464d3adf06 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1193,7 +1193,7 @@ impl<'tcx> Ty<'tcx> { /// Fast path helper for testing if a type is `Freeze`. /// - /// Returning true means the type is known to be `Freeze`. Returning + /// Returning `true` means the type is known to be `Freeze`. Returning /// `false` means nothing -- could be `Freeze`, might not be. pub fn is_trivially_freeze(self) -> bool { match self.kind() { @@ -1227,16 +1227,18 @@ impl<'tcx> Ty<'tcx> { } } - /// Checks whether values of this type `T` implement 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)) + /// Checks whether values of this type `T` implement the `UnsafeUnpin` trait. + /// + /// For more information, see [RFC 3467](https://rust-lang.github.io/rfcs/3467-unsafe-pinned.html). + pub fn is_unsafe_unpin(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { + self.is_trivially_unsafe_unpin() || tcx.is_unsafe_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 `UnsafeUnpin`. /// - /// Returning true means the type is known to be `Unpin`. Returning - /// `false` means nothing -- could be `Unpin`, might not be. - fn is_trivially_unpin(self) -> bool { + /// Returning `true` means the type is known to be `UnsafeUnpin`. Returning + /// `false` means nothing -- could be `UnsafeUnpin`, might not be. + fn is_trivially_unsafe_unpin(self) -> bool { match self.kind() { ty::Int(_) | ty::Uint(_) @@ -1250,8 +1252,8 @@ impl<'tcx> Ty<'tcx> { | ty::FnDef(..) | ty::Error(_) | ty::FnPtr(..) => true, - ty::Tuple(fields) => fields.iter().all(Self::is_trivially_unpin), - ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_unpin(), + ty::Tuple(fields) => fields.iter().all(Self::is_trivially_unsafe_unpin), + ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_unsafe_unpin(), ty::Adt(..) | ty::Bound(..) | ty::Closure(..) diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 46fae5c6fa465..4a90ec3830e83 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1060,11 +1060,15 @@ where ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"), - // Coroutines have one special built-in candidate, `Unpin`, which - // takes precedence over the structural auto trait candidate being - // assembled. + // Coroutines have two special built-in candidates, `UnsafeUnpin` + // and `Unpin`, which take precedence over the structural auto trait + // candidate being assembled. ty::Coroutine(def_id, _) - if self.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) => + if self.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) + || self.cx().is_lang_item( + goal.predicate.def_id(), + TraitSolverLangItem::UnsafeUnpin, + ) => { match self.cx().coroutine_movability(def_id) { Movability::Static => Some(Err(NoSolution)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bb59b4c40bd7f..bd8c5b8cc0169 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2152,6 +2152,8 @@ symbols! { unsafe_fields, unsafe_no_drop_flag, unsafe_pin_internals, + unsafe_pinned, + unsafe_unpin, unsize, unsized_const_param_ty, unsized_const_params, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e495bdbf7825c..23455a72c1e3e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -727,16 +727,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.ambiguous = true; } ty::Coroutine(coroutine_def_id, _) - if self.tcx().is_lang_item(def_id, LangItem::Unpin) => + if self.tcx().is_lang_item(def_id, LangItem::Unpin) + || self.tcx().is_lang_item(def_id, LangItem::UnsafeUnpin) => { match self.tcx().coroutine_movability(coroutine_def_id) { hir::Movability::Static => { - // Immovable coroutines are never `Unpin`, so - // suppress the normal auto-impl candidate for it. + // Immovable coroutines are never `[Unsafe]Unpin`, + // so suppress the normal auto-impl candidate for it. } hir::Movability::Movable => { - // Movable coroutines are always `Unpin`, so add an - // unconditional builtin candidate. + // Movable coroutines are always `[Unsafe]Unpin`, + // so add an unconditional builtin candidate. candidates.vec.push(BuiltinCandidate { has_nested: false }); } } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 332b00e8423bb..46c907ce68a39 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -379,7 +379,7 @@ fn adjust_for_rust_scalar<'tcx>( Some(kind) } else if let Some(pointee) = drop_target_pointee { // The argument to `drop_in_place` is semantically equivalent to a mutable reference. - Some(PointerKind::MutableRef { unpin: pointee.is_unpin(tcx, cx.typing_env) }) + Some(PointerKind::MutableRef { unpin: pointee.is_unsafe_unpin(tcx, cx.typing_env) }) } else { None }; @@ -387,11 +387,13 @@ fn adjust_for_rust_scalar<'tcx>( attrs.pointee_align = Some(pointee.align); // `Box` are not necessarily dereferenceable for the entire duration of the function as - // they can be deallocated at any time. Same for non-frozen shared references (see - // ), and for mutable references to - // potentially self-referential types (see - // ). If LLVM had a way - // to say "dereferenceable on entry" we could use it here. + // they can be deallocated at any time. Same for `!Freeze` shared references + // (see ), and for mutable + // references to `!UnsafeUnpin` (ie. potentially self-referential types) + // (see + // and ). + // + // If LLVM had a way to say "dereferenceable on entry" we could use it here. attrs.pointee_size = match kind { PointerKind::Box { .. } | PointerKind::SharedRef { frozen: false } diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs index 2157ab3c4022c..7b6302c76b933 100644 --- a/compiler/rustc_ty_utils/src/common_traits.rs +++ b/compiler/rustc_ty_utils/src/common_traits.rs @@ -18,8 +18,11 @@ fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, is_item_raw(tcx, query, LangItem::Freeze) } -fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - is_item_raw(tcx, query, LangItem::Unpin) +fn is_unsafe_unpin_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, +) -> bool { + is_item_raw(tcx, query, LangItem::UnsafeUnpin) } fn is_item_raw<'tcx>( @@ -33,5 +36,6 @@ fn is_item_raw<'tcx>( } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers }; + *providers = + Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unsafe_unpin_raw, ..*providers }; } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index ce8dc3a2515d4..39bc1c4c43a21 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -619,6 +619,9 @@ fn layout_of_uncached<'tcx>( )); } + // UnsafeCell and UnsafePinned both disable niche optimizations + let is_special_no_niche = def.is_unsafe_cell() || def.is_unsafe_pinned(); + let get_discriminant_type = |min, max| abi::Integer::repr_discr(tcx, ty, &def.repr(), min, max); @@ -647,7 +650,7 @@ fn layout_of_uncached<'tcx>( &def.repr(), &variants, def.is_enum(), - def.is_unsafe_cell(), + is_special_no_niche, tcx.layout_scalar_valid_range(def.did()), get_discriminant_type, discriminants_iter(), @@ -673,7 +676,7 @@ fn layout_of_uncached<'tcx>( &def.repr(), &variants, def.is_enum(), - def.is_unsafe_cell(), + is_special_no_niche, tcx.layout_scalar_valid_range(def.did()), get_discriminant_type, discriminants_iter(), @@ -1068,6 +1071,7 @@ fn coroutine_layout<'tcx>( // would do the same for us here. // See , . // FIXME: Remove when is implemented and aliased coroutine fields are wrapped in `UnsafePinned`. + // FIXME(unsafe_pinned) largest_niche: None, size, align, diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index eeb80bc3ab420..39e6929b6be5e 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -37,6 +37,7 @@ pub enum TraitSolverLangItem { TransmuteTrait, Tuple, Unpin, + UnsafeUnpin, Unsize, // tidy-alphabetical-end } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 49ce1bbcf3980..a4b8a6a619cf5 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -139,6 +139,7 @@ #![feature(ub_checks)] #![feature(unchecked_neg)] #![feature(unchecked_shifts)] +#![feature(unsafe_pinned)] #![feature(utf16_extra)] #![feature(variant_count)] // tidy-alphabetical-end diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 029c8b356d074..51c69525ab9e1 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -17,6 +17,7 @@ use crate::cell::UnsafeCell; use crate::cmp; use crate::fmt::Debug; use crate::hash::{Hash, Hasher}; +use crate::pin::UnsafePinned; /// Implements a given marker trait for multiple types at the same time. /// @@ -858,6 +859,20 @@ marker_impls! { {T: ?Sized} &mut T, } +/// Used to determine whether a type contains any `UnsafePinned` +/// (or `PhantomPinned`) internally, but not through an indirection. This +/// affects, for example, if we emit `noalias` metadata for `&mut T` or not. +#[cfg_attr(not(bootstrap), lang = "unsafe_unpin")] +#[cfg_attr(bootstrap, allow(dead_code))] +pub(crate) unsafe auto trait UnsafeUnpin {} + +impl !UnsafeUnpin for UnsafePinned {} +unsafe impl UnsafeUnpin for PhantomData {} +unsafe impl UnsafeUnpin for *const T {} +unsafe impl UnsafeUnpin for *mut T {} +unsafe impl UnsafeUnpin for &T {} +unsafe impl UnsafeUnpin for &mut T {} + /// Types that do not require any pinning guarantees. /// /// For information on what "pinning" is, see the [`pin` module] documentation. @@ -933,6 +948,11 @@ pub auto trait Unpin {} /// A marker type which does not implement `Unpin`. /// /// If a type contains a `PhantomPinned`, it will not implement `Unpin` by default. +/// +/// Note that for backwards compatibility with the new [`UnsafePinned`] wrapper +/// type, placing this marker in your struct acts as if you wrapped the entire +/// struct in an `UnsafePinned`. This type will likely eventually be deprecated, +/// and all new code should be using `UnsafePinned` instead. #[stable(feature = "pin", since = "1.33.0")] #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct PhantomPinned; @@ -940,6 +960,12 @@ pub struct PhantomPinned; #[stable(feature = "pin", since = "1.33.0")] impl !Unpin for PhantomPinned {} +// This is a small hack to allow existing code which uses PhantomPinned to +// opt-out of noalias to continue working. Ideally PhantomPinned could just +// wrap an `UnsafePinned<()>` to get the same effect, but then it wouldn't be a +// unit struct. +impl !UnsafeUnpin for PhantomPinned {} + marker_impls! { #[stable(feature = "pin", since = "1.33.0")] Unpin for diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 2a0bf89fcf7a9..fc0c9a7776cca 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -931,6 +931,11 @@ use crate::{ }; use crate::{cmp, fmt}; +mod unsafe_pinned; + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +pub use self::unsafe_pinned::UnsafePinned; + /// A pointer which pins its pointee in place. /// /// [`Pin`] is a wrapper around some kind of pointer `Ptr` which makes that pointer "pin" its diff --git a/library/core/src/pin/unsafe_pinned.rs b/library/core/src/pin/unsafe_pinned.rs new file mode 100644 index 0000000000000..df8cc63701fa6 --- /dev/null +++ b/library/core/src/pin/unsafe_pinned.rs @@ -0,0 +1,187 @@ +use crate::fmt; +use crate::marker::{PointerLike, Unpin}; +use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::pin::Pin; + +/// The type `UnsafePinned` lets unsafe code violate +/// the rule that `&mut UnsafePinned` may never alias anything else. +/// +/// However, even if you define your type like `pub struct Wrapper(UnsafePinned<...>)`, +/// it is still very risky to have an `&mut Wrapper` that aliases +/// anything else. Many functions that work generically on `&mut T` assume that the +/// memory that stores `T` is uniquely owned (such as `mem::swap`). In other words, +/// while having aliasing with `&mut Wrapper` is not immediate Undefined +/// Behavior, it is still unsound to expose such a mutable reference to code you do +/// not control! Techniques such as pinning via `Pin` are needed to ensure soundness. +/// +/// Similar to `UnsafeCell`, `UnsafePinned` will not usually show up in the public +/// API of a library. It is an internal implementation detail of libraries that +/// need to support aliasing mutable references. +/// +/// Further note that this does *not* lift the requirement that shared references +/// must be read-only! Use `UnsafeCell` for that. +/// +/// This type blocks niches the same way `UnsafeCell` does. +#[cfg_attr(not(bootstrap), lang = "unsafe_pinned")] +#[repr(transparent)] +#[unstable(feature = "unsafe_pinned", issue = "125735")] +pub struct UnsafePinned { + value: T, +} + +/// When this type is used, that almost certainly means safe APIs need to use +/// pinning to avoid the aliases from becoming invalidated. Therefore let's mark +/// this as `!Unpin`. You can always opt back in to `Unpin` with an `impl` +/// block, provided your API is still sound while unpinned. +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl !Unpin for UnsafePinned {} + +/// The type is `Copy` when `T` is to avoid people assuming that `Copy` implies there +/// is no `UnsafePinned` anywhere. (This is an issue with `UnsafeCell`: people use `Copy` bounds +/// to mean `Freeze`.) Given that there is no `unsafe impl Copy for ...`, this is also +/// the option that leaves the user more choices (as they can always wrap this in a `!Copy` type). +// FIXME(unsafe_pinned): this may be unsound or a footgun? +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl Copy for UnsafePinned {} + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl Clone for UnsafePinned { + fn clone(&self) -> Self { + *self + } +} + +// `Send` and `Sync` are inherited from `T`. This is similar to `SyncUnsafeCell`, since +// we eventually concluded that `UnsafeCell` implicitly making things `!Sync` is sometimes +// unergonomic. A type that needs to be `!Send`/`!Sync` should really have an explicit +// opt-out itself, e.g. via an `PhantomData<*mut T>` or (one day) via `impl !Send`/`impl !Sync`. + +impl UnsafePinned { + /// Constructs a new instance of `UnsafePinned` which will wrap the + /// specified value. + /// + /// All access to the inner value through `&UnsafePinned` or + /// `&mut UnsafePinned` or `Pin<&mut UnsafePinned>` requires `unsafe` + /// code. + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn new(value: T) -> Self { + UnsafePinned { value } + } + + /// Unwraps the value, consuming this `UnsafePinned`. + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + #[rustc_allow_const_fn_unstable(const_precise_live_drops)] + pub const fn into_inner(self) -> T { + self.value + } +} + +impl UnsafePinned { + /// Get read-write access to the contents of a pinned `UnsafePinned`. + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn get_mut_pinned(self: Pin<&mut Self>) -> *mut T { + // SAFETY: we're not using `get_unchecked_mut` to unpin anything + unsafe { self.get_unchecked_mut() as *mut Self as *mut T } + } + + /// Get read-write access to the contents of an `UnsafePinned`. + /// + /// You should usually be using `get_mut_pinned` instead to explicitly track + /// the fact that this memory is "pinned" due to there being aliases. + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn get_mut_unchecked(&mut self) -> *mut T { + self as *mut Self as *mut T + } + + /// Get read-only access to the contents of a shared `UnsafePinned`. + /// Note that `&UnsafePinned` is read-only if `&T` is read-only. + /// This means that if there is mutation of the `T`, future reads from the + /// `*const T` returned here are UB! + /// + /// ```rust,no_run + /// #![feature(unsafe_pinned)] + /// use std::pin::UnsafePinned; + /// + /// unsafe { + /// let mut x = UnsafePinned::new(0); + /// let ptr = x.get(); // read-only pointer, assumes immutability + /// x.get_mut_unchecked().write(1); + /// ptr.read(); // UB! + /// } + /// ``` + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn get(&self) -> *const T { + self as *const Self as *const T + } + + /// meow + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn raw_get(this: *const Self) -> *const T { + this as *const T + } + + /// meow + #[inline(always)] + #[must_use] + #[unstable(feature = "unsafe_pinned", issue = "125735")] + pub const fn raw_get_mut(this: *mut Self) -> *mut T { + this as *mut T + } +} + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl Default for UnsafePinned { + /// Creates an `UnsafePinned`, with the `Default` value for T. + fn default() -> Self { + UnsafePinned::new(T::default()) + } +} + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl From for UnsafePinned { + /// Creates a new `UnsafePinned` containing the given value. + fn from(value: T) -> Self { + UnsafePinned::new(value) + } +} + +#[unstable(feature = "unsafe_pinned", issue = "125735")] +impl fmt::Debug for UnsafePinned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UnsafePinned").finish_non_exhaustive() + } +} + +#[unstable(feature = "coerce_unsized", issue = "18598")] +// #[unstable(feature = "unsafe_pinned", issue = "125735")] +impl, U> CoerceUnsized> for UnsafePinned {} + +// Allow types that wrap `UnsafePinned` to also implement `DispatchFromDyn` +// and become dyn-compatible method receivers. +// Note that currently `UnsafePinned` itself cannot be a method receiver +// because it does not implement Deref. +// In other words: +// `self: UnsafePinned<&Self>` won't work +// `self: UnsafePinned` becomes possible +// FIXME(unsafe_pinned) this logic is copied from UnsafeCell, is it still sound? +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +// #[unstable(feature = "unsafe_pinned", issue = "125735")] +impl, U> DispatchFromDyn> for UnsafePinned {} + +#[unstable(feature = "pointer_like_trait", issue = "none")] +// #[unstable(feature = "unsafe_pinned", issue = "125735")] +impl PointerLike for UnsafePinned {} + +// FIXME(unsafe_pinned): impl PinCoerceUnsized for UnsafePinned? diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 18a5a0612bb06..d60f19935d87d 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -70,7 +70,7 @@ impl NewPermission { access: None, protector: None, } - } else if pointee.is_unpin(*cx.tcx, cx.typing_env()) { + } else if 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, @@ -78,7 +78,7 @@ impl NewPermission { protector, } } else { - // `!Unpin` dereferences do not get `noalias` nor `dereferenceable`. + // `!UnsafeUnpin` dereferences do not get `noalias` nor `dereferenceable`. NewPermission::Uniform { perm: Permission::SharedReadWrite, access: None, @@ -128,7 +128,7 @@ 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_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 { @@ -137,7 +137,7 @@ impl NewPermission { protector: (kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector), } } else { - // `!Unpin` boxes do not get `noalias` nor `dereferenceable`. + // `!UnsafeUnpin` boxes do not get `noalias` nor `dereferenceable`. NewPermission::Uniform { perm: Permission::SharedReadWrite, access: None, diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index f39a606513d5c..c9934b064da9d 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -134,7 +134,7 @@ impl<'tcx> NewPermission { cx: &crate::MiriInterpCx<'tcx>, ) -> Option { let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env()); - let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env()); + let ty_is_unsafe_unpin = pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env()); let is_protected = kind == RetagKind::FnEntry; // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`, // interior mutability and protectors interact poorly. @@ -142,11 +142,12 @@ impl<'tcx> NewPermission { // in the case of a protected reference: protected references are always considered // "freeze" in their reservation phase. let initial_state = match mutability { - Mutability::Mut if ty_is_unpin => Permission::new_reserved(ty_is_freeze, is_protected), + Mutability::Mut if ty_is_unsafe_unpin => + Permission::new_reserved(ty_is_freeze, is_protected), Mutability::Not if ty_is_freeze => Permission::new_frozen(), // Raw pointers never enter this function so they are not handled. // However raw pointers are not the only pointers that take the parent - // tag, this also happens for `!Unpin` `&mut`s and interior mutable + // tag, this also happens for `!UnsafeUnpin` `&mut`s and `!Freeze` // `&`s, which are excluded above. _ => return None, }; @@ -165,8 +166,8 @@ impl<'tcx> NewPermission { zero_size: bool, ) -> Option { let pointee = ty.builtin_deref(true).unwrap(); - pointee.is_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| { - // Regular `Unpin` box, give it `noalias` but only a weak protector + pointee.is_unsafe_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| { + // Regular `UnsafeUnpin` box, give it `noalias` but only a weak protector // because it is valid to deallocate it within the function. let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.typing_env()); let protected = kind == RetagKind::FnEntry; diff --git a/src/tools/miri/tests/pass/stacked-borrows/coroutine-self-referential.rs b/src/tools/miri/tests/pass/stacked-borrows/coroutine-self-referential.rs index 72e360fe19a1f..b47c1f560c5c1 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/coroutine-self-referential.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/coroutine-self-referential.rs @@ -1,5 +1,5 @@ // See https://github.com/rust-lang/unsafe-code-guidelines/issues/148: -// this fails when Stacked Borrows is strictly applied even to `!Unpin` types. +// this fails when Stacked Borrows is strictly applied even to `!UnsafeUnpin` types. #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; diff --git a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs index 4261f411eea47..b09b38851540f 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs @@ -19,7 +19,7 @@ fn main() { array_casts(); mut_below_shr(); wide_raw_ptr_in_tuple(); - not_unpin_not_protected(); + not_unsafe_unpin_not_protected(); write_does_not_invalidate_all_aliases(); box_into_raw_allows_interior_mutable_alias(); } @@ -234,10 +234,9 @@ fn wide_raw_ptr_in_tuple() { r.type_id(); } -fn not_unpin_not_protected() { - // `&mut !Unpin`, at least for now, does not get `noalias` nor `dereferenceable`, so we also - // don't add protectors. (We could, but until we have a better idea for where we want to go with - // the self-referential-coroutine situation, it does not seem worth the potential trouble.) +fn not_unsafe_unpin_not_protected() { + // `&mut !UnsafeUnpin`, does not get `noalias` nor `dereferenceable`, so we + // also don't add protectors. use std::marker::PhantomPinned; pub struct NotUnpin(#[allow(dead_code)] i32, PhantomPinned); diff --git a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs index b9d5ca06ed058..75fa743ed9b9b 100644 --- a/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs +++ b/src/tools/miri/tests/pass/tree_borrows/tree-borrows.rs @@ -28,7 +28,7 @@ fn main() { array_casts(); mut_below_shr(); wide_raw_ptr_in_tuple(); - not_unpin_not_protected(); + not_unsafe_unpin_not_protected(); write_does_not_invalidate_all_aliases(); } @@ -311,10 +311,9 @@ fn wide_raw_ptr_in_tuple() { r.type_id(); } -fn not_unpin_not_protected() { - // `&mut !Unpin`, at least for now, does not get `noalias` nor `dereferenceable`, so we also - // don't add protectors. (We could, but until we have a better idea for where we want to go with - // the self-referential-coroutine situation, it does not seem worth the potential trouble.) +fn not_unsafe_unpin_not_protected() { + // `&mut !UnsafeUnpin`, does not get `noalias` nor `dereferenceable`, so we + // also don't add protectors. use std::marker::PhantomPinned; pub struct NotUnpin(#[allow(dead_code)] i32, PhantomPinned); diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs index 1a211dfe096b3..3aa4edad2e815 100644 --- a/tests/codegen/function-arguments.rs +++ b/tests/codegen/function-arguments.rs @@ -128,6 +128,7 @@ pub fn mutable_borrow_ret() -> &'static mut i32 { // This one is *not* `noalias` because it might be self-referential. // It is also not `dereferenceable` due to // . +// FIXME(unsafe_pinned) pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {} // CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) diff --git a/tests/ui/lazy-type-alias-impl-trait/freeze_cycle.rs b/tests/ui/lazy-type-alias-impl-trait/freeze_cycle.rs index 3f8567c3057a8..01ade153d13ce 100644 --- a/tests/ui/lazy-type-alias-impl-trait/freeze_cycle.rs +++ b/tests/ui/lazy-type-alias-impl-trait/freeze_cycle.rs @@ -1,12 +1,12 @@ //@ check-pass -#![feature(coroutine_trait, negative_impls)] +#![feature(coroutine_trait)] +use std::future::Future; use std::ops::{Coroutine, CoroutineState}; -use std::task::{Poll, Context}; -use std::future::{Future}; -use std::ptr::NonNull; use std::pin::Pin; +use std::ptr::NonNull; +use std::task::{Context, Poll}; fn main() {} @@ -23,10 +23,6 @@ where { struct GenFuture>(T); - // We rely on the fact that async/await futures are immovable in order to create - // self-referential borrows in the underlying coroutine. - impl> !Unpin for GenFuture {} - impl> Future for GenFuture { type Output = T::Return; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {