diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3a81b93d157ce..1d108b4a3ad2a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -28,6 +28,7 @@ use rustc_data_structures::packed::Pu128; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; @@ -2269,10 +2270,32 @@ impl TyKind { /// Syntax used to declare a trait object. #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[repr(u8)] pub enum TraitObjectSyntax { - Dyn, - DynStar, - None, + // SAFETY: When adding new variants make sure to update the `Tag` impl. + Dyn = 0, + DynStar = 1, + None = 2, +} + +/// SAFETY: `TraitObjectSyntax` only has 3 data-less variants which means +/// it can be represented with a `u2`. We use `repr(u8)` to guarantee the +/// discriminants of the variants are no greater than `3`. +unsafe impl Tag for TraitObjectSyntax { + const BITS: u32 = 2; + + fn into_usize(self) -> usize { + self as u8 as usize + } + + unsafe fn from_usize(tag: usize) -> Self { + match tag { + 0 => TraitObjectSyntax::Dyn, + 1 => TraitObjectSyntax::DynStar, + 2 => TraitObjectSyntax::None, + _ => unreachable!(), + } + } } #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index fe2d5a594f312..ea2e4c4bc3adb 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -47,6 +47,7 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::tagged_ptr::CovariantCopyTaggedPtr; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; @@ -1157,7 +1158,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let lifetime_bound = this.elided_dyn_bound(t.span); (bounds, lifetime_bound) }); - let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None); + let kind = hir::TyKind::TraitObject( + bounds, + CovariantCopyTaggedPtr::new(lifetime_bound, TraitObjectSyntax::None), + ); return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() }; } @@ -1308,7 +1312,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span)); (bounds, lifetime_bound) }); - hir::TyKind::TraitObject(bounds, lifetime_bound, *kind) + hir::TyKind::TraitObject(bounds, CovariantCopyTaggedPtr::new(lifetime_bound, *kind)) } TyKind::ImplTrait(def_node_id, bounds) => { let span = t.span; @@ -2364,8 +2368,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id = self.next_id(); hir::TyKind::TraitObject( arena_vec![self; principal], - self.elided_dyn_bound(span), - TraitObjectSyntax::None, + CovariantCopyTaggedPtr::new( + self.elided_dyn_bound(span), + TraitObjectSyntax::None, + ), ) } _ => hir::TyKind::Path(hir::QPath::Resolved(None, path)), diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3555009c63f4a..9da9929d91b37 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -893,7 +893,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if alias_ty.span.desugaring_kind().is_some() { // Skip `async` desugaring `impl Future`. } - if let TyKind::TraitObject(_, lt, _) = alias_ty.kind { + if let TyKind::TraitObject(_, lt) = alias_ty.kind { if lt.ident.name == kw::Empty { spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string())); } else { diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 2b629024bfe45..f19a3f56ba51d 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -73,6 +73,7 @@ impl_dyn_send!( [Box where T: ?Sized + DynSend, A: std::alloc::Allocator + DynSend] [crate::sync::RwLock where T: DynSend] [crate::tagged_ptr::CopyTaggedPtr where P: Send + crate::tagged_ptr::Pointer, T: Send + crate::tagged_ptr::Tag, const CP: bool] + [crate::tagged_ptr::CovariantCopyTaggedPtr where P: Send + crate::tagged_ptr::Pointer, T: Send + crate::tagged_ptr::Tag, const CP: bool] [rustc_arena::TypedArena where T: DynSend] [indexmap::IndexSet where V: DynSend, S: DynSend] [indexmap::IndexMap where K: DynSend, V: DynSend, S: DynSend] @@ -149,6 +150,7 @@ impl_dyn_sync!( [crate::sync::WorkerLocal where T: DynSend] [crate::intern::Interned<'a, T> where 'a, T: DynSync] [crate::tagged_ptr::CopyTaggedPtr where P: Sync + crate::tagged_ptr::Pointer, T: Sync + crate::tagged_ptr::Tag, const CP: bool] + [crate::tagged_ptr::CovariantCopyTaggedPtr where P: Sync + crate::tagged_ptr::Pointer, T: Sync + crate::tagged_ptr::Tag, const CP: bool] [parking_lot::lock_api::Mutex where R: DynSync, T: ?Sized + DynSend] [parking_lot::lock_api::RwLock where R: DynSync, T: ?Sized + DynSend + DynSync] [indexmap::IndexSet where V: DynSync, S: DynSync] diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs index 2914eece6796b..bff3c7e2e584f 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -23,10 +23,12 @@ use std::sync::Arc; use crate::aligned::Aligned; mod copy; +mod covariant; mod drop; mod impl_tag; pub use copy::CopyTaggedPtr; +pub use covariant::CovariantCopyTaggedPtr; pub use drop::TaggedPtr; /// This describes the pointer type encapsulated by [`TaggedPtr`] and diff --git a/compiler/rustc_data_structures/src/tagged_ptr/covariant.rs b/compiler/rustc_data_structures/src/tagged_ptr/covariant.rs new file mode 100644 index 0000000000000..98fc7d545fffe --- /dev/null +++ b/compiler/rustc_data_structures/src/tagged_ptr/covariant.rs @@ -0,0 +1,335 @@ +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; +use std::mem::ManuallyDrop; +use std::num::NonZero; +use std::ops::{Deref, DerefMut}; +use std::ptr::NonNull; + +use super::{Pointer, Tag}; +use crate::stable_hasher::{HashStable, StableHasher}; + +/// A covariant [`Copy`] tagged pointer. +/// +/// This is essentially `{ pointer: P, tag: T }` packed in a single pointer. +/// +/// You should use this instead of the [`super::CopyTaggedPtr`] type in cases where +/// subtyping on the pointer type is necessary. +/// +/// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without +/// unpacking. Otherwise we don't implement [`PartialEq`], [`Eq`] and [`Hash`]; +/// if you want that, wrap the [`super::CopyTaggedPtr`]. +/// +/// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr +pub struct CovariantCopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + /// This is semantically a pair of `pointer: P` and `tag: T` fields, + /// however we pack them in a single pointer, to save space. + /// + /// We pack the tag into the **most**-significant bits of the pointer to + /// ease retrieval of the value. A left shift is a multiplication and + /// those are embeddable in instruction encoding, for example: + /// + /// ```asm + /// // () + /// example::shift_read3: + /// mov eax, dword ptr [8*rdi] + /// ret + /// + /// example::mask_read3: + /// and rdi, -8 + /// mov eax, dword ptr [rdi] + /// ret + /// ``` + /// + /// This is ASM outputted by rustc for reads of values behind tagged + /// pointers for different approaches of tagging: + /// - `shift_read3` uses `<< 3` (the tag is in the most-significant bits) + /// - `mask_read3` uses `& !0b111` (the tag is in the least-significant bits) + /// + /// The shift approach thus produces less instructions and is likely faster + /// (see ). + /// + /// Encoding diagram: + /// ```text + /// [ packed.addr ] + /// [ tag ] [ pointer.addr >> T::BITS ] <-- usize::BITS - T::BITS bits + /// ^ + /// | + /// T::BITS bits + /// ``` + /// + /// The tag can be retrieved by `packed.addr() >> T::BITS` and the pointer + /// can be retrieved by `packed.map_addr(|addr| addr << T::BITS)`. + packed: NonNull<()>, + tag_pointer_ghost: PhantomData<(P, T)>, +} + +// Note that even though `CopyTaggedPtr` is only really expected to work with +// `P: Copy`, can't add `P: Copy` bound, because `CopyTaggedPtr` is used in the +// `TaggedPtr`'s implementation. +impl CovariantCopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + /// Tags `pointer` with `tag`. + /// + /// Note that this leaks `pointer`: it won't be dropped when + /// `CopyTaggedPtr` is dropped. If you have a pointer with a significant + /// drop, use [`TaggedPtr`] instead. + /// + /// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr + #[inline] + pub fn new(pointer: P, tag: T) -> Self { + Self { + packed: Self::pack(P::into_ptr(pointer), tag).cast::<()>(), + tag_pointer_ghost: PhantomData, + } + } + + /// Retrieves the pointer. + #[inline] + pub fn pointer(self) -> P + where + P: Copy, + { + // SAFETY: pointer_raw returns the original pointer + // + // Note that this isn't going to double-drop or anything because we have + // P: Copy + unsafe { P::from_ptr(self.pointer_raw()) } + } + + /// Retrieves the tag. + #[inline] + pub fn tag(&self) -> T { + // Unpack the tag, according to the `self.packed` encoding scheme + let tag = self.packed.addr().get() >> Self::TAG_BIT_SHIFT; + + // Safety: + // The shift retrieves the original value from `T::into_usize`, + // satisfying `T::from_usize`'s preconditions. + unsafe { T::from_usize(tag) } + } + + /// Sets the tag to a new value. + #[inline] + pub fn set_tag(&mut self, tag: T) { + self.packed = Self::pack(self.pointer_raw(), tag).cast::<()>(); + } + + const TAG_BIT_SHIFT: u32 = usize::BITS - T::BITS; + const ASSERTION: () = { assert!(T::BITS <= P::BITS) }; + + /// Pack pointer `ptr` that comes from [`P::into_ptr`] with a `tag`, + /// according to `self.packed` encoding scheme. + /// + /// [`P::into_ptr`]: Pointer::into_ptr + #[inline] + fn pack(ptr: NonNull, tag: T) -> NonNull { + // Trigger assert! + let () = Self::ASSERTION; + + let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT; + + ptr.map_addr(|addr| { + // Safety: + // - The pointer is `NonNull` => it's address is `NonZero` + // - `P::BITS` least significant bits are always zero (`Pointer` contract) + // - `T::BITS <= P::BITS` (from `Self::ASSERTION`) + // + // Thus `addr >> T::BITS` is guaranteed to be non-zero. + // + // `{non_zero} | packed_tag` can't make the value zero. + + let packed = (addr.get() >> T::BITS) | packed_tag; + unsafe { NonZero::new_unchecked(packed) } + }) + } + + /// Retrieves the original raw pointer from `self.packed`. + #[inline] + pub(super) fn pointer_raw(&self) -> NonNull { + self.packed + .map_addr(|addr| unsafe { NonZero::new_unchecked(addr.get() << T::BITS) }) + .cast::() + } + + /// This provides a reference to the `P` pointer itself, rather than the + /// `Deref::Target`. It is used for cases where we want to call methods + /// that may be implement differently for the Pointer than the Pointee + /// (e.g., `Rc::clone` vs cloning the inner value). + pub(super) fn with_pointer_ref(&self, f: impl FnOnce(&P) -> R) -> R { + // Safety: + // - `self.raw.pointer_raw()` is originally returned from `P::into_ptr` + // and as such is valid for `P::from_ptr`. + // - This also allows us to not care whatever `f` panics or not. + // - Even though we create a copy of the pointer, we store it inside + // `ManuallyDrop` and only access it by-ref, so we don't double-drop. + // + // Semantically this is just `f(&self.pointer)` (where `self.pointer` + // is non-packed original pointer). + // + // Note that even though `CopyTaggedPtr` is only really expected to + // work with `P: Copy`, we have to assume `P: ?Copy`, because + // `CopyTaggedPtr` is used in the `TaggedPtr`'s implementation. + let ptr = unsafe { ManuallyDrop::new(P::from_ptr(self.pointer_raw())) }; + f(&ptr) + } +} + +impl Copy for CovariantCopyTaggedPtr +where + P: Pointer + Copy, + T: Tag, +{ +} + +impl Clone for CovariantCopyTaggedPtr +where + P: Pointer + Copy, + T: Tag, +{ + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Deref for CovariantCopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + type Target = P::Target; + + #[inline] + fn deref(&self) -> &Self::Target { + // Safety: + // `pointer_raw` returns the original pointer from `P::into_ptr` which, + // by the `Pointer`'s contract, must be valid. + unsafe { self.pointer_raw().as_ref() } + } +} + +impl DerefMut for CovariantCopyTaggedPtr +where + P: Pointer + DerefMut, + T: Tag, +{ + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + // Safety: + // `pointer_raw` returns the original pointer from `P::into_ptr` which, + // by the `Pointer`'s contract, must be valid for writes if + // `P: DerefMut`. + unsafe { self.pointer_raw().as_mut() } + } +} + +impl fmt::Debug for CovariantCopyTaggedPtr +where + P: Pointer + fmt::Debug, + T: Tag + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.with_pointer_ref(|ptr| { + f.debug_struct("CopyTaggedPtr").field("pointer", ptr).field("tag", &self.tag()).finish() + }) + } +} + +impl PartialEq for CovariantCopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] + fn eq(&self, other: &Self) -> bool { + self.packed == other.packed + } +} + +impl Eq for CovariantCopyTaggedPtr +where + P: Pointer, + T: Tag, +{ +} + +impl Hash for CovariantCopyTaggedPtr +where + P: Pointer, + T: Tag, +{ + #[inline] + fn hash(&self, state: &mut H) { + self.packed.hash(state); + } +} + +impl HashStable for CovariantCopyTaggedPtr +where + P: Pointer + HashStable, + T: Tag + HashStable, +{ + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { + self.with_pointer_ref(|ptr| ptr.hash_stable(hcx, hasher)); + self.tag().hash_stable(hcx, hasher); + } +} + +// Safety: +// `CopyTaggedPtr` is semantically just `{ ptr: P, tag: T }`, as such +// it's ok to implement `Sync` as long as `P: Sync, T: Sync` +unsafe impl Sync for CovariantCopyTaggedPtr +where + P: Sync + Pointer, + T: Sync + Tag, +{ +} + +// Safety: +// `CopyTaggedPtr` is semantically just `{ ptr: P, tag: T }`, as such +// it's ok to implement `Send` as long as `P: Send, T: Send` +unsafe impl Send for CovariantCopyTaggedPtr +where + P: Send + Pointer, + T: Send + Tag, +{ +} + +/// Test that `new` does not compile if there is not enough alignment for the +/// tag in the pointer. +/// +/// ```compile_fail,E0080 +/// use rustc_data_structures::tagged_ptr::{CopyTaggedPtr, Tag}; +/// +/// #[derive(Copy, Clone, Debug, PartialEq, Eq)] +/// enum Tag2 { B00 = 0b00, B01 = 0b01, B10 = 0b10, B11 = 0b11 }; +/// +/// unsafe impl Tag for Tag2 { +/// const BITS: u32 = 2; +/// +/// fn into_usize(self) -> usize { todo!() } +/// unsafe fn from_usize(tag: usize) -> Self { todo!() } +/// } +/// +/// let value = 12u16; +/// let reference = &value; +/// let tag = Tag2::B01; +/// +/// let _ptr = CopyTaggedPtr::<_, _, true>::new(reference, tag); +/// ``` +// For some reason miri does not get the compile error +// probably it `check`s instead of `build`ing? +#[cfg(not(miri))] +const _: () = (); + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_data_structures/src/tagged_ptr/covariant/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/covariant/tests.rs new file mode 100644 index 0000000000000..cd76e28f45313 --- /dev/null +++ b/compiler/rustc_data_structures/src/tagged_ptr/covariant/tests.rs @@ -0,0 +1,53 @@ +use std::ptr; + +use crate::hashes::Hash128; +use crate::stable_hasher::{HashStable, StableHasher}; +use crate::tagged_ptr::{CovariantCopyTaggedPtr, Pointer, Tag, Tag2}; + +#[test] +fn smoke() { + let value = 12u32; + let reference = &value; + let tag = Tag2::B01; + + let ptr = tag_ptr(reference, tag); + + assert_eq!(ptr.tag(), tag); + assert_eq!(*ptr, 12); + assert!(ptr::eq(ptr.pointer(), reference)); + + let copy = ptr; + + let mut ptr = ptr; + ptr.set_tag(Tag2::B00); + assert_eq!(ptr.tag(), Tag2::B00); + + assert_eq!(copy.tag(), tag); + assert_eq!(*copy, 12); + assert!(ptr::eq(copy.pointer(), reference)); +} + +#[test] +fn stable_hash_hashes_as_tuple() { + let hash_packed = { + let mut hasher = StableHasher::new(); + tag_ptr(&12, Tag2::B11).hash_stable(&mut (), &mut hasher); + hasher.finish::() + }; + + let hash_tupled = { + let mut hasher = StableHasher::new(); + (&12, Tag2::B11).hash_stable(&mut (), &mut hasher); + hasher.finish::() + }; + + assert_eq!(hash_packed, hash_tupled); +} + +/// Helper to create tagged pointers without specifying `COMPARE_PACKED` if it does not matter. +fn tag_ptr, T: Tag>( + ptr: P, + tag: T, +) -> CovariantCopyTaggedPtr { + CovariantCopyTaggedPtr::new(ptr, tag) +} diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6b4b716d9a802..c34db350d2130 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -15,6 +15,7 @@ pub use rustc_ast::{ }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::tagged_ptr::CovariantCopyTaggedPtr; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; @@ -760,11 +761,8 @@ impl<'hir> Generics<'hir> { && let [.., segment] = trait_ref.path.segments && let Some(ret_ty) = segment.args().paren_sugar_output() && let ret_ty = ret_ty.peel_refs() - && let TyKind::TraitObject( - _, - _, - TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar, - ) = ret_ty.kind + && let TyKind::TraitObject(_, tagged_ptr) = ret_ty.kind + && let TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar = tagged_ptr.tag() && ret_ty.span.can_be_used_for_suggestions() { Some(ret_ty.span) @@ -2871,6 +2869,7 @@ impl<'hir> AssocItemConstraintKind<'hir> { } #[derive(Debug, Clone, Copy, HashStable_Generic)] +#[repr(C)] pub struct Ty<'hir> { pub hir_id: HirId, pub kind: TyKind<'hir>, @@ -3150,6 +3149,7 @@ pub enum InferDelegationKind { /// The various kinds of types recognized by the compiler. #[derive(Debug, Clone, Copy, HashStable_Generic)] +#[repr(u8)] pub enum TyKind<'hir> { /// Actual type should be inherited from `DefId` signature InferDelegation(DefId, InferDelegationKind), @@ -3180,7 +3180,10 @@ pub enum TyKind<'hir> { TraitAscription(GenericBounds<'hir>), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), + TraitObject( + &'hir [PolyTraitRef<'hir>], + CovariantCopyTaggedPtr<&'hir Lifetime, TraitObjectSyntax, true>, + ), /// Unused for now. Typeof(&'hir AnonConst), /// `TyKind::Infer` means the type should be inferred instead of it having been diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 85e555d903b9a..e0a66220fecc5 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -906,7 +906,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_const_arg(length)); } - TyKind::TraitObject(bounds, ref lifetime, _syntax) => { + TyKind::TraitObject(bounds, ref lifetime) => { for bound in bounds { try_visit!(visitor.visit_poly_trait_ref(bound)); } diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index e8706d1adfbb6..0fa70c9857a6e 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -810,7 +810,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { intravisit::walk_ty(this, ty); }); } - hir::TyKind::TraitObject(bounds, lifetime, _) => { + hir::TyKind::TraitObject(bounds, lifetime) => { + let lifetime = lifetime.pointer(); + debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { @@ -827,7 +829,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // use the object lifetime defaulting // rules. So e.g., `Box` becomes // `Box`. - self.resolve_object_lifetime_default(lifetime) + self.resolve_object_lifetime_default(&*lifetime) } LifetimeName::Infer => { // If the user writes `'_`, we use the *ordinary* elision @@ -838,7 +840,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } LifetimeName::Param(..) | LifetimeName::Static => { // If the user wrote an explicit name, use that. - self.visit_lifetime(lifetime); + self.visit_lifetime(&*lifetime); } LifetimeName::Error => {} } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index b7d3617fbe739..aeb9c8af17a49 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -21,9 +21,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Option { let tcx = self.tcx(); - let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = + let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) = self_ty.kind - else { + && let TraitObjectSyntax::None = tagged_ptr.tag() + { + poly_trait_ref + } else { return None; }; @@ -287,7 +290,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let (dyn_str, paren_dyn_str) = if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { + let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { // There are more than one trait bound, we need surrounding parentheses. vec![ (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2154568c51267..ba3d679d61c25 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2325,7 +2325,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.late_bound_vars(hir_ty.hir_id), ), ), - hir::TyKind::TraitObject(bounds, lifetime, repr) => { + hir::TyKind::TraitObject(bounds, tagged_ptr) => { + let lifetime = tagged_ptr.pointer(); + let repr = tagged_ptr.tag(); + if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { // Don't continue with type analysis if the `dyn` keyword is missing // It generates confusing errors, especially if the user meant to use another diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 42c98ab434ef7..6c986dab2c58f 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -401,7 +401,8 @@ impl<'a> State<'a> { self.print_bounds("impl", bounds); } hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), - hir::TyKind::TraitObject(bounds, lifetime, syntax) => { + hir::TyKind::TraitObject(bounds, lifetime) => { + let syntax = lifetime.tag(); match syntax { ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"), ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"), @@ -420,7 +421,7 @@ impl<'a> State<'a> { if !lifetime.is_elided() { self.nbsp(); self.word_space("+"); - self.print_lifetime(lifetime); + self.print_lifetime(lifetime.pointer()); } } hir::TyKind::Array(ty, ref length) => { diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index a9797c3b32a0e..ddb4cdb640933 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -111,7 +111,9 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { } fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { - let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; + let hir::TyKind::TraitObject(bounds, _lifetime_and_syntax_pointer) = &ty.kind else { + return; + }; for bound in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 77745599afb92..cb5a326cb50d7 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -572,16 +572,16 @@ pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { match ty.kind { - hir::TyKind::TraitObject( - _, - hir::Lifetime { + hir::TyKind::TraitObject(_, tagged_ptr) + if let hir::Lifetime { res: hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, .. - }, - _, - ) - | hir::TyKind::OpaqueDef(..) => self.0.push(ty), + } = tagged_ptr.pointer() => + { + self.0.push(ty) + } + hir::TyKind::OpaqueDef(..) => self.0.push(ty), _ => {} } hir::intravisit::walk_ty(self, ty); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3416a17624e91..f136bd2010312 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -374,7 +374,7 @@ pub fn suggest_new_region_bound( } } } - TyKind::TraitObject(_, lt, _) => { + TyKind::TraitObject(_, lt) => { if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), @@ -592,11 +592,9 @@ pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec, pub DefId); impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { - if let TyKind::TraitObject( - poly_trait_refs, - Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. }, - _, - ) = t.kind + if let TyKind::TraitObject(poly_trait_refs, lifetime_ptr) = t.kind + && let Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. } = + lifetime_ptr.pointer() { for ptr in poly_trait_refs { if Some(self.1) == ptr.trait_ref.trait_def_id() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 7ba87e180d0bc..c3e54142be435 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -585,8 +585,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.tcx.hir_node_by_def_id(obligation.cause.body_id) && let hir::ItemKind::Impl(impl_) = item.kind && let None = impl_.of_trait - && let hir::TyKind::TraitObject(_, _, syntax) = impl_.self_ty.kind - && let TraitObjectSyntax::None = syntax + && let hir::TyKind::TraitObject(_, tagged_ptr) = impl_.self_ty.kind + && let TraitObjectSyntax::None = tagged_ptr.tag() && impl_.self_ty.span.edition().at_least_rust_2021() { // Silence the dyn-compatibility error in favor of the missing dyn on diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 9d85ca1dd4dd7..63a436b3dffe9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3075,7 +3075,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if let Some(ty) = ty { match ty.kind { - hir::TyKind::TraitObject(traits, _, _) => { + hir::TyKind::TraitObject(traits, _) => { let (span, kw) = match traits { [first, ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2ed2df799dd1c..d13ad2de203cf 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1837,10 +1837,10 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) } TyKind::Path(_) => clean_qpath(ty, cx), - TyKind::TraitObject(bounds, lifetime, _) => { + TyKind::TraitObject(bounds, lifetime) => { let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect(); let lifetime = - if !lifetime.is_elided() { Some(clean_lifetime(lifetime, cx)) } else { None }; + if !lifetime.is_elided() { Some(clean_lifetime(lifetime.pointer(), cx)) } else { None }; DynTrait(bounds, lifetime) } TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))), diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 239822f408569..b4f6b123472e0 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -433,7 +433,7 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); }, - TyKind::TraitObject(bounds, lt, _) => { + TyKind::TraitObject(bounds, lt) => { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index bde88ab61adf4..760c5d3ecfedf 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -47,7 +47,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Originally reported as the issue #3128. let inner_snippet = snippet(cx, inner.span, ".."); let suggestion = match &inner.kind { - TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { + TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => { format!("&{ltopt}({inner_snippet})") }, TyKind::Path(qpath) diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs index b89bd6a8d0589..65830a781b714 100644 --- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs @@ -52,7 +52,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { // function types bring a lot of overhead TyKind::BareFn(bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), - TyKind::TraitObject(param_bounds, _, _) => { + TyKind::TraitObject(param_bounds, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { bound .bound_generic_params diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 68e7f807bf514..026be11aec90f 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -400,7 +400,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), - TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) => (Pat::Str("dyn"), Pat::Str("")), + TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => (Pat::Str("dyn"), Pat::Str("")), // NOTE: `TraitObject` is incomplete. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), } diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index ed52c481de124..3b11db588d4e1 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1247,7 +1247,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, TyKind::Path(qpath) => self.hash_qpath(qpath), - TyKind::TraitObject(_, lifetime, _) => { + TyKind::TraitObject(_, lifetime) => { self.hash_lifetime(lifetime); }, TyKind::Typeof(anon_const) => {