about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core.rs66
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core.rs66
-rw-r--r--compiler/rustc_data_structures/src/aligned.rs4
-rw-r--r--compiler/rustc_data_structures/src/lib.rs4
-rw-r--r--compiler/rustc_data_structures/src/marker.rs30
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir/src/lang_items.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs25
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs346
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs53
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs9
-rw-r--r--compiler/rustc_lint/src/multiple_supertrait_upcastable.rs3
-rw-r--r--compiler/rustc_middle/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/query/keys.rs8
-rw-r--r--compiler/rustc_middle/src/query/mod.rs11
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs21
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs22
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs1
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs36
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs26
-rw-r--r--compiler/rustc_middle/src/ty/util.rs4
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs19
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs55
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/effect_goals.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs62
-rw-r--r--compiler/rustc_serialize/src/lib.rs17
-rw-r--r--compiler/rustc_serialize/src/serialize.rs4
-rw-r--r--compiler/rustc_smir/src/lib.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs3
-rw-r--r--compiler/rustc_span/src/symbol.rs3
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs5
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs91
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs41
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs30
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs57
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs8
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs82
-rw-r--r--compiler/rustc_type_ir/src/elaborate.rs38
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs8
-rw-r--r--compiler/rustc_type_ir/src/lang_items.rs2
-rw-r--r--compiler/rustc_type_ir/src/solve/mod.rs22
51 files changed, 1001 insertions, 391 deletions
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs
index 012e4dbc3ec..524ebde1c74 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs
@@ -14,8 +14,14 @@
 #![no_core]
 #![allow(dead_code, internal_features, ambiguous_wide_pointer_comparisons)]
 
+#[lang = "pointee_sized"]
+pub trait PointeeSized {}
+
+#[lang = "meta_sized"]
+pub trait MetaSized: PointeeSized {}
+
 #[lang = "sized"]
-pub trait Sized {}
+pub trait Sized: MetaSized {}
 
 #[lang = "destruct"]
 pub trait Destruct {}
@@ -24,35 +30,35 @@ pub trait Destruct {}
 pub trait Tuple {}
 
 #[lang = "unsize"]
-pub trait Unsize<T: ?Sized> {}
+pub trait Unsize<T: PointeeSized>: PointeeSized {}
 
 #[lang = "coerce_unsized"]
 pub trait CoerceUnsized<T> {}
 
-impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
-impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
-impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
-impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+impl<'a, 'b: 'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a U> for &'b T {}
+impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a mut U> for &'a mut T {}
+impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for *const T {}
+impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*mut U> for *mut T {}
 
 #[lang = "dispatch_from_dyn"]
 pub trait DispatchFromDyn<T> {}
 
 // &T -> &U
-impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
+impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a U> for &'a T {}
 // &mut T -> &mut U
-impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
+impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a mut U> for &'a mut T {}
 // *const T -> *const U
-impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
+impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*const U> for *const T {}
 // *mut T -> *mut U
-impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
-impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}
+impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*mut U> for *mut T {}
+impl<T: MetaSized + Unsize<U>, U: MetaSized> DispatchFromDyn<Box<U>> for Box<T> {}
 
 #[lang = "legacy_receiver"]
 pub trait LegacyReceiver {}
 
-impl<T: ?Sized> LegacyReceiver for &T {}
-impl<T: ?Sized> LegacyReceiver for &mut T {}
-impl<T: ?Sized> LegacyReceiver for Box<T> {}
+impl<T: PointeeSized> LegacyReceiver for &T {}
+impl<T: PointeeSized> LegacyReceiver for &mut T {}
+impl<T: MetaSized> LegacyReceiver for Box<T> {}
 
 #[lang = "copy"]
 pub trait Copy {}
@@ -74,9 +80,9 @@ impl Copy for isize {}
 impl Copy for f32 {}
 impl Copy for f64 {}
 impl Copy for char {}
-impl<'a, T: ?Sized> Copy for &'a T {}
-impl<T: ?Sized> Copy for *const T {}
-impl<T: ?Sized> Copy for *mut T {}
+impl<'a, T: PointeeSized> Copy for &'a T {}
+impl<T: PointeeSized> Copy for *const T {}
+impl<T: PointeeSized> Copy for *mut T {}
 impl<T: Copy> Copy for Option<T> {}
 
 #[lang = "sync"]
@@ -94,17 +100,17 @@ unsafe impl Sync for i32 {}
 unsafe impl Sync for isize {}
 unsafe impl Sync for char {}
 unsafe impl Sync for f32 {}
-unsafe impl<'a, T: ?Sized> Sync for &'a T {}
+unsafe impl<'a, T: PointeeSized> Sync for &'a T {}
 unsafe impl<T: Sync, const N: usize> Sync for [T; N] {}
 
 #[lang = "freeze"]
 unsafe auto trait Freeze {}
 
-unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
-unsafe impl<T: ?Sized> Freeze for *const T {}
-unsafe impl<T: ?Sized> Freeze for *mut T {}
-unsafe impl<T: ?Sized> Freeze for &T {}
-unsafe impl<T: ?Sized> Freeze for &mut T {}
+unsafe impl<T: PointeeSized> Freeze for PhantomData<T> {}
+unsafe impl<T: PointeeSized> Freeze for *const T {}
+unsafe impl<T: PointeeSized> Freeze for *mut T {}
+unsafe impl<T: PointeeSized> Freeze for &T {}
+unsafe impl<T: PointeeSized> Freeze for &mut T {}
 
 #[lang = "structural_peq"]
 pub trait StructuralPartialEq {}
@@ -443,7 +449,7 @@ pub enum Option<T> {
 pub use Option::*;
 
 #[lang = "phantom_data"]
-pub struct PhantomData<T: ?Sized>;
+pub struct PhantomData<T: PointeeSized>;
 
 #[lang = "fn_once"]
 #[rustc_paren_sugar]
@@ -564,18 +570,18 @@ pub trait Deref {
 #[repr(transparent)]
 #[rustc_layout_scalar_valid_range_start(1)]
 #[rustc_nonnull_optimization_guaranteed]
-pub struct NonNull<T: ?Sized>(pub *const T);
+pub struct NonNull<T: PointeeSized>(pub *const T);
 
-impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
-impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
+impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
+impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
 
-pub struct Unique<T: ?Sized> {
+pub struct Unique<T: PointeeSized> {
     pub pointer: NonNull<T>,
     pub _marker: PhantomData<T>,
 }
 
-impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
-impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
+impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
+impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
 
 #[lang = "global_alloc_ty"]
 pub struct Global;
diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs
index aca1f0080ef..9dfb12be243 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core.rs
@@ -19,8 +19,14 @@ unsafe extern "C" fn _Unwind_Resume() {
     intrinsics::unreachable();
 }
 
+#[lang = "pointee_sized"]
+pub trait PointeeSized {}
+
+#[lang = "meta_sized"]
+pub trait MetaSized: PointeeSized {}
+
 #[lang = "sized"]
-pub trait Sized {}
+pub trait Sized: MetaSized {}
 
 #[lang = "destruct"]
 pub trait Destruct {}
@@ -29,35 +35,35 @@ pub trait Destruct {}
 pub trait Tuple {}
 
 #[lang = "unsize"]
-pub trait Unsize<T: ?Sized> {}
+pub trait Unsize<T: PointeeSized>: PointeeSized {}
 
 #[lang = "coerce_unsized"]
 pub trait CoerceUnsized<T> {}
 
-impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
-impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
-impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
-impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+impl<'a, 'b: 'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a U> for &'b T {}
+impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a mut U> for &'a mut T {}
+impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*const U> for *const T {}
+impl<T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<*mut U> for *mut T {}
 
 #[lang = "dispatch_from_dyn"]
 pub trait DispatchFromDyn<T> {}
 
 // &T -> &U
-impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
+impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a U> for &'a T {}
 // &mut T -> &mut U
-impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
+impl<'a, T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<&'a mut U> for &'a mut T {}
 // *const T -> *const U
-impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
+impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*const U> for *const T {}
 // *mut T -> *mut U
-impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
-impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U, ()>> for Box<T, ()> {}
+impl<T: PointeeSized + Unsize<U>, U: PointeeSized> DispatchFromDyn<*mut U> for *mut T {}
+impl<T: MetaSized + Unsize<U>, U: MetaSized> DispatchFromDyn<Box<U, ()>> for Box<T, ()> {}
 
 #[lang = "legacy_receiver"]
 pub trait LegacyReceiver {}
 
-impl<T: ?Sized> LegacyReceiver for &T {}
-impl<T: ?Sized> LegacyReceiver for &mut T {}
-impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {}
+impl<T: PointeeSized> LegacyReceiver for &T {}
+impl<T: PointeeSized> LegacyReceiver for &mut T {}
+impl<T: MetaSized> LegacyReceiver for Box<T> {}
 
 #[lang = "receiver"]
 trait Receiver {}
@@ -84,9 +90,9 @@ impl Copy for i128 {}
 impl Copy for f32 {}
 impl Copy for f64 {}
 impl Copy for char {}
-impl<'a, T: ?Sized> Copy for &'a T {}
-impl<T: ?Sized> Copy for *const T {}
-impl<T: ?Sized> Copy for *mut T {}
+impl<'a, T: PointeeSized> Copy for &'a T {}
+impl<T: PointeeSized> Copy for *const T {}
+impl<T: PointeeSized> Copy for *mut T {}
 
 #[lang = "sync"]
 pub unsafe trait Sync {}
@@ -102,17 +108,17 @@ unsafe impl Sync for i16 {}
 unsafe impl Sync for i32 {}
 unsafe impl Sync for isize {}
 unsafe impl Sync for char {}
-unsafe impl<'a, T: ?Sized> Sync for &'a T {}
+unsafe impl<'a, T: PointeeSized> Sync for &'a T {}
 unsafe impl Sync for [u8; 16] {}
 
 #[lang = "freeze"]
 unsafe auto trait Freeze {}
 
-unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
-unsafe impl<T: ?Sized> Freeze for *const T {}
-unsafe impl<T: ?Sized> Freeze for *mut T {}
-unsafe impl<T: ?Sized> Freeze for &T {}
-unsafe impl<T: ?Sized> Freeze for &mut T {}
+unsafe impl<T: PointeeSized> Freeze for PhantomData<T> {}
+unsafe impl<T: PointeeSized> Freeze for *const T {}
+unsafe impl<T: PointeeSized> Freeze for *mut T {}
+unsafe impl<T: PointeeSized> Freeze for &T {}
+unsafe impl<T: PointeeSized> Freeze for &mut T {}
 
 #[lang = "structural_peq"]
 pub trait StructuralPartialEq {}
@@ -456,7 +462,7 @@ pub enum Option<T> {
 pub use Option::*;
 
 #[lang = "phantom_data"]
-pub struct PhantomData<T: ?Sized>;
+pub struct PhantomData<T: PointeeSized>;
 
 #[lang = "fn_once"]
 #[rustc_paren_sugar]
@@ -576,18 +582,18 @@ impl Allocator for Global {}
 #[repr(transparent)]
 #[rustc_layout_scalar_valid_range_start(1)]
 #[rustc_nonnull_optimization_guaranteed]
-pub struct NonNull<T: ?Sized>(pub *const T);
+pub struct NonNull<T: PointeeSized>(pub *const T);
 
-impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
-impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
+impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
+impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
 
-pub struct Unique<T: ?Sized> {
+pub struct Unique<T: PointeeSized> {
     pub pointer: NonNull<T>,
     pub _marker: PhantomData<T>,
 }
 
-impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
-impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
+impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
+impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
 
 #[lang = "owned_box"]
 pub struct Box<T: ?Sized, A: Allocator = Global>(Unique<T>, A);
diff --git a/compiler/rustc_data_structures/src/aligned.rs b/compiler/rustc_data_structures/src/aligned.rs
index a636d09fcae..111740e5509 100644
--- a/compiler/rustc_data_structures/src/aligned.rs
+++ b/compiler/rustc_data_structures/src/aligned.rs
@@ -1,5 +1,7 @@
 use std::ptr::Alignment;
 
+use rustc_serialize::PointeeSized;
+
 /// Returns the ABI-required minimum alignment of a type in bytes.
 ///
 /// This is equivalent to [`align_of`], but also works for some unsized
@@ -17,7 +19,7 @@ pub const fn align_of<T: ?Sized + Aligned>() -> Alignment {
 /// example `[T]` has alignment of `T`.
 ///
 /// [`align_of::<Self>()`]: align_of
-pub unsafe trait Aligned {
+pub unsafe trait Aligned: PointeeSized {
     /// Alignment of `Self`.
     const ALIGN: Alignment;
 }
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index eb3817a80a7..0431182e9e2 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -12,6 +12,7 @@
 #![allow(rustc::potential_query_instability)]
 #![cfg_attr(bootstrap, feature(cfg_match))]
 #![cfg_attr(not(bootstrap), feature(cfg_select))]
+#![cfg_attr(not(bootstrap), feature(sized_hierarchy))]
 #![deny(unsafe_op_in_unsafe_fn)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
@@ -43,6 +44,9 @@ use std::fmt;
 pub use atomic_ref::AtomicRef;
 pub use ena::{snapshot_vec, undo_log, unify};
 pub use rustc_index::static_assert_size;
+// re-exported for `rustc_smir`
+// FIXME(sized_hierarchy): remove with `cfg(bootstrap)`, see `rustc_serialize/src/lib.rs`
+pub use rustc_serialize::PointeeSized;
 
 pub mod aligned;
 pub mod base_n;
diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs
index e0df1b232e1..4846bc997f1 100644
--- a/compiler/rustc_data_structures/src/marker.rs
+++ b/compiler/rustc_data_structures/src/marker.rs
@@ -1,5 +1,7 @@
 use std::alloc::Allocator;
 
+use rustc_serialize::PointeeSized;
+
 #[diagnostic::on_unimplemented(message = "`{Self}` doesn't implement `DynSend`. \
             Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Send`")]
 // This is an auto trait for types which can be sent across threads if `sync::is_dyn_thread_safe()`
@@ -15,7 +17,7 @@ pub unsafe auto trait DynSend {}
 pub unsafe auto trait DynSync {}
 
 // Same with `Sync` and `Send`.
-unsafe impl<T: DynSync + ?Sized> DynSend for &T {}
+unsafe impl<T: DynSync + ?Sized + PointeeSized> DynSend for &T {}
 
 macro_rules! impls_dyn_send_neg {
     ($([$t1: ty $(where $($generics1: tt)*)?])*) => {
@@ -27,9 +29,9 @@ macro_rules! impls_dyn_send_neg {
 impls_dyn_send_neg!(
     [std::env::Args]
     [std::env::ArgsOs]
-    [*const T where T: ?Sized]
-    [*mut T where T: ?Sized]
-    [std::ptr::NonNull<T> where T: ?Sized]
+    [*const T where T: ?Sized + PointeeSized]
+    [*mut T where T: ?Sized + PointeeSized]
+    [std::ptr::NonNull<T> where T: ?Sized + PointeeSized]
     [std::rc::Rc<T, A> where T: ?Sized, A: Allocator]
     [std::rc::Weak<T, A> where T: ?Sized, A: Allocator]
     [std::sync::MutexGuard<'_, T> where T: ?Sized]
@@ -100,12 +102,12 @@ macro_rules! impls_dyn_sync_neg {
 impls_dyn_sync_neg!(
     [std::env::Args]
     [std::env::ArgsOs]
-    [*const T where T: ?Sized]
-    [*mut T where T: ?Sized]
+    [*const T where T: ?Sized + PointeeSized]
+    [*mut T where T: ?Sized + PointeeSized]
     [std::cell::Cell<T> where T: ?Sized]
     [std::cell::RefCell<T> where T: ?Sized]
     [std::cell::UnsafeCell<T> where T: ?Sized]
-    [std::ptr::NonNull<T> where T: ?Sized]
+    [std::ptr::NonNull<T> where T: ?Sized + PointeeSized]
     [std::rc::Rc<T, A> where T: ?Sized, A: Allocator]
     [std::rc::Weak<T, A> where T: ?Sized, A: Allocator]
     [std::cell::OnceCell<T> where T]
@@ -175,10 +177,10 @@ impl_dyn_sync!(
     [thin_vec::ThinVec<T> where T: DynSync]
 );
 
-pub fn assert_dyn_sync<T: ?Sized + DynSync>() {}
-pub fn assert_dyn_send<T: ?Sized + DynSend>() {}
-pub fn assert_dyn_send_val<T: ?Sized + DynSend>(_t: &T) {}
-pub fn assert_dyn_send_sync_val<T: ?Sized + DynSync + DynSend>(_t: &T) {}
+pub fn assert_dyn_sync<T: ?Sized + PointeeSized + DynSync>() {}
+pub fn assert_dyn_send<T: ?Sized + PointeeSized + DynSend>() {}
+pub fn assert_dyn_send_val<T: ?Sized + PointeeSized + DynSend>(_t: &T) {}
+pub fn assert_dyn_send_sync_val<T: ?Sized + PointeeSized + DynSync + DynSend>(_t: &T) {}
 
 #[derive(Copy, Clone)]
 pub struct FromDyn<T>(T);
@@ -231,10 +233,10 @@ impl<T> std::ops::DerefMut for FromDyn<T> {
 // an instance of `DynSend` and `DynSync`, since the compiler cannot infer
 // it automatically in some cases. (e.g. Box<dyn Send / Sync>)
 #[derive(Copy, Clone)]
-pub struct IntoDynSyncSend<T: ?Sized>(pub T);
+pub struct IntoDynSyncSend<T: ?Sized + PointeeSized>(pub T);
 
-unsafe impl<T: ?Sized + Send> DynSend for IntoDynSyncSend<T> {}
-unsafe impl<T: ?Sized + Sync> DynSync for IntoDynSyncSend<T> {}
+unsafe impl<T: ?Sized + PointeeSized + Send> DynSend for IntoDynSyncSend<T> {}
+unsafe impl<T: ?Sized + PointeeSized + Sync> DynSync for IntoDynSyncSend<T> {}
 
 impl<T> std::ops::Deref for IntoDynSyncSend<T> {
     type Target = T;
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index bd6ea850147..5e42b919f9d 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -237,6 +237,8 @@ declare_features! (
     (internal, profiler_runtime, "1.18.0", None),
     /// Allows using `rustc_*` attributes (RFC 572).
     (internal, rustc_attrs, "1.0.0", None),
+    /// Introduces a hierarchy of `Sized` traits (RFC 3729).
+    (unstable, sized_hierarchy, "CURRENT_RUSTC_VERSION", None),
     /// Allows using the `#[stable]` and `#[unstable]` attributes.
     (internal, staged_api, "1.0.0", None),
     /// Added for testing unstable lints; perma-unstable.
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 21d36ed54cd..3a08e5ae336 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -165,6 +165,8 @@ pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> {
 language_item_table! {
 //  Variant name,            Name,                     Getter method name,         Target                  Generic requirements;
     Sized,                   sym::sized,               sized_trait,                Target::Trait,          GenericRequirement::Exact(0);
+    MetaSized,               sym::meta_sized,          meta_sized_trait,           Target::Trait,          GenericRequirement::Exact(0);
+    PointeeSized,            sym::pointee_sized,       pointee_sized_trait,        Target::Trait,          GenericRequirement::Exact(0);
     Unsize,                  sym::unsize,              unsize_trait,               Target::Trait,          GenericRequirement::Minimum(1);
     /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
     StructuralPeq,           sym::structural_peq,      structural_peq_trait,       Target::Trait,          GenericRequirement::None;
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index c5c7e6b2aa7..5b8aa28102c 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -76,6 +76,7 @@ pub use check::{check_abi, check_abi_fn_ptr, check_custom_abi};
 use rustc_abi::{ExternAbi, VariantIdx};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
+use rustc_hir::LangItem;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_index::bit_set::DenseBitSet;
@@ -331,7 +332,7 @@ fn bounds_from_generic_predicates<'tcx>(
             ty::ClauseKind::Trait(trait_predicate) => {
                 let entry = types.entry(trait_predicate.self_ty()).or_default();
                 let def_id = trait_predicate.def_id();
-                if !tcx.is_default_trait(def_id) {
+                if !tcx.is_default_trait(def_id) && !tcx.is_lang_item(def_id, LangItem::Sized) {
                     // Do not add that restriction to the list if it is a positive requirement.
                     entry.push(trait_predicate.def_id());
                 }
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index b8dc01cbc03..20d0e87b7a7 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1258,6 +1258,11 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuarant
     debug!(?item.owner_id);
 
     let def_id = item.owner_id.def_id;
+    if tcx.is_lang_item(def_id.into(), LangItem::PointeeSized) {
+        // `PointeeSized` is removed during lowering.
+        return Ok(());
+    }
+
     let trait_def = tcx.trait_def(def_id);
     if trait_def.is_marker
         || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker)
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 5f1cdeddc19..53c44cdc411 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -44,6 +44,14 @@ fn associated_type_bounds<'tcx>(
             | PredicateFilter::SelfOnly
             | PredicateFilter::SelfTraitThatDefines(_)
             | PredicateFilter::SelfAndAssociatedTypeBounds => {
+                icx.lowerer().add_sizedness_bounds(
+                    &mut bounds,
+                    item_ty,
+                    hir_bounds,
+                    None,
+                    None,
+                    span,
+                );
                 icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
             }
             // `ConstIfConst` is only interested in `~const` bounds.
@@ -333,6 +341,14 @@ fn opaque_type_bounds<'tcx>(
             | PredicateFilter::SelfOnly
             | PredicateFilter::SelfTraitThatDefines(_)
             | PredicateFilter::SelfAndAssociatedTypeBounds => {
+                icx.lowerer().add_sizedness_bounds(
+                    &mut bounds,
+                    item_ty,
+                    hir_bounds,
+                    None,
+                    None,
+                    span,
+                );
                 icx.lowerer().add_default_traits(&mut bounds, item_ty, hir_bounds, None, span);
             }
             //`ConstIfConst` is only interested in `~const` bounds.
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index ce0f83d0ec2..c337765c5fe 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -162,7 +162,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                         .map(|t| ty::Binder::dummy(t.instantiate_identity()));
                 }
             }
-
             ItemKind::Trait(_, _, _, _, self_bounds, ..)
             | ItemKind::TraitAlias(_, _, self_bounds) => {
                 is_trait = Some((self_bounds, item.span));
@@ -183,21 +182,29 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // and the explicit where-clauses, but to get the full set of predicates
     // on a trait we must also consider the bounds that follow the trait's name,
     // like `trait Foo: A + B + C`.
-    if let Some(self_bounds) = is_trait {
+    if let Some((self_bounds, span)) = is_trait {
         let mut bounds = Vec::new();
         icx.lowerer().lower_bounds(
             tcx.types.self_param,
-            self_bounds.0,
+            self_bounds,
             &mut bounds,
             ty::List::empty(),
             PredicateFilter::All,
         );
+        icx.lowerer().add_sizedness_bounds(
+            &mut bounds,
+            tcx.types.self_param,
+            self_bounds,
+            None,
+            Some(def_id),
+            span,
+        );
         icx.lowerer().add_default_super_traits(
             def_id,
             &mut bounds,
-            self_bounds.0,
+            self_bounds,
             hir_generics,
-            self_bounds.1,
+            span,
         );
         predicates.extend(bounds);
     }
@@ -224,6 +231,14 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                 let param_ty = icx.lowerer().lower_ty_param(param.hir_id);
                 let mut bounds = Vec::new();
                 // Implicit bounds are added to type params unless a `?Trait` bound is found
+                icx.lowerer().add_sizedness_bounds(
+                    &mut bounds,
+                    param_ty,
+                    &[],
+                    Some((param.def_id, hir_generics.predicates)),
+                    None,
+                    param.span,
+                );
                 icx.lowerer().add_default_traits(
                     &mut bounds,
                     param_ty,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 106420faa4c..ea1dfdfd806 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -4,15 +4,15 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::codes::*;
 use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
-use rustc_hir::AmbigArg;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{AmbigArg, LangItem, PolyTraitRef};
 use rustc_middle::bug;
 use rustc_middle::ty::{
     self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
     TypeVisitor, Upcast,
 };
-use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
+use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw};
 use rustc_trait_selection::traits;
 use smallvec::SmallVec;
 use tracing::{debug, instrument};
@@ -23,23 +23,213 @@ use crate::hir_ty_lowering::{
     AssocItemQSelf, FeedConstTy, HirTyLowerer, PredicateFilter, RegionInferReason,
 };
 
+#[derive(Debug, Default)]
+struct CollectedBound {
+    /// `Trait`
+    positive: bool,
+    /// `?Trait`
+    maybe: bool,
+    /// `!Trait`
+    negative: bool,
+}
+
+impl CollectedBound {
+    /// Returns `true` if any of `Trait`, `?Trait` or `!Trait` were encountered.
+    fn any(&self) -> bool {
+        self.positive || self.maybe || self.negative
+    }
+}
+
+#[derive(Debug)]
+struct CollectedSizednessBounds {
+    // Collected `Sized` bounds
+    sized: CollectedBound,
+    // Collected `MetaSized` bounds
+    meta_sized: CollectedBound,
+    // Collected `PointeeSized` bounds
+    pointee_sized: CollectedBound,
+}
+
+impl CollectedSizednessBounds {
+    /// Returns `true` if any of `Trait`, `?Trait` or `!Trait` were encountered for `Sized`,
+    /// `MetaSized` or `PointeeSized`.
+    fn any(&self) -> bool {
+        self.sized.any() || self.meta_sized.any() || self.pointee_sized.any()
+    }
+}
+
+fn search_bounds_for<'tcx>(
+    hir_bounds: &'tcx [hir::GenericBound<'tcx>],
+    self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    mut f: impl FnMut(&'tcx PolyTraitRef<'tcx>),
+) {
+    let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| {
+        for hir_bound in hir_bounds {
+            let hir::GenericBound::Trait(ptr) = hir_bound else {
+                continue;
+            };
+
+            f(ptr)
+        }
+    };
+
+    search_bounds(hir_bounds);
+    if let Some((self_ty, where_clause)) = self_ty_where_predicates {
+        for clause in where_clause {
+            if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind
+                && pred.is_param_bound(self_ty.to_def_id())
+            {
+                search_bounds(pred.bounds);
+            }
+        }
+    }
+}
+
+fn collect_unbounds<'tcx>(
+    hir_bounds: &'tcx [hir::GenericBound<'tcx>],
+    self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+) -> SmallVec<[&'tcx PolyTraitRef<'tcx>; 1]> {
+    let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
+    search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| {
+        if matches!(ptr.modifiers.polarity, hir::BoundPolarity::Maybe(_)) {
+            unbounds.push(ptr);
+        }
+    });
+    unbounds
+}
+
+fn collect_bounds<'a, 'tcx>(
+    hir_bounds: &'a [hir::GenericBound<'tcx>],
+    self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    target_did: DefId,
+) -> CollectedBound {
+    let mut collect_into = CollectedBound::default();
+    search_bounds_for(hir_bounds, self_ty_where_predicates, |ptr| {
+        if !matches!(ptr.trait_ref.path.res, Res::Def(DefKind::Trait, did) if did == target_did) {
+            return;
+        }
+
+        match ptr.modifiers.polarity {
+            hir::BoundPolarity::Maybe(_) => collect_into.maybe = true,
+            hir::BoundPolarity::Negative(_) => collect_into.negative = true,
+            hir::BoundPolarity::Positive => collect_into.positive = true,
+        }
+    });
+    collect_into
+}
+
+fn collect_sizedness_bounds<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    hir_bounds: &'tcx [hir::GenericBound<'tcx>],
+    self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+    span: Span,
+) -> CollectedSizednessBounds {
+    let sized_did = tcx.require_lang_item(LangItem::Sized, span);
+    let sized = collect_bounds(hir_bounds, self_ty_where_predicates, sized_did);
+
+    let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span);
+    let meta_sized = collect_bounds(hir_bounds, self_ty_where_predicates, meta_sized_did);
+
+    let pointee_sized_did = tcx.require_lang_item(LangItem::PointeeSized, span);
+    let pointee_sized = collect_bounds(hir_bounds, self_ty_where_predicates, pointee_sized_did);
+
+    CollectedSizednessBounds { sized, meta_sized, pointee_sized }
+}
+
+/// Add a trait bound for `did`.
+fn add_trait_bound<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
+    self_ty: Ty<'tcx>,
+    did: DefId,
+    span: Span,
+) {
+    let trait_ref = ty::TraitRef::new(tcx, did, [self_ty]);
+    // Preferable to put sizedness obligations first, since we report better errors for `Sized`
+    // ambiguity.
+    bounds.insert(0, (trait_ref.upcast(tcx), span));
+}
+
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
-    pub(crate) fn add_default_traits(
+    /// Skip `PointeeSized` bounds.
+    ///
+    /// `PointeeSized` is a "fake bound" insofar as anywhere a `PointeeSized` bound exists, there
+    /// is actually the absence of any bounds. This avoids limitations around non-global where
+    /// clauses being preferred over item bounds (where `PointeeSized` bounds would be
+    /// proven) - which can result in errors when a `PointeeSized` supertrait/bound/predicate is
+    /// added to some items.
+    pub(crate) fn should_skip_sizedness_bound<'hir>(
+        &self,
+        bound: &'hir hir::GenericBound<'tcx>,
+    ) -> bool {
+        bound
+            .trait_ref()
+            .and_then(|tr| tr.trait_def_id())
+            .map(|did| self.tcx().is_lang_item(did, LangItem::PointeeSized))
+            .unwrap_or(false)
+    }
+
+    /// Adds sizedness bounds to a trait, trait alias, parameter, opaque type or associated type.
+    ///
+    /// - On parameters, opaque type and associated types, add default `Sized` bound if no explicit
+    ///   sizedness bounds are present.
+    /// - On traits and trait aliases, add default `MetaSized` supertrait if no explicit sizedness
+    ///   bounds are present.
+    /// - On parameters, opaque type, associated types and trait aliases, add a `MetaSized` bound if
+    ///   a `?Sized` bound is present.
+    pub(crate) fn add_sizedness_bounds(
         &self,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         self_ty: Ty<'tcx>,
-        hir_bounds: &[hir::GenericBound<'tcx>],
+        hir_bounds: &'tcx [hir::GenericBound<'tcx>],
         self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+        trait_did: Option<LocalDefId>,
         span: Span,
     ) {
-        self.add_default_traits_with_filter(
-            bounds,
-            self_ty,
-            hir_bounds,
-            self_ty_where_predicates,
-            span,
-            |_| true,
-        );
+        let tcx = self.tcx();
+
+        let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span);
+        let pointee_sized_did = tcx.require_lang_item(LangItem::PointeeSized, span);
+
+        // If adding sizedness bounds to a trait, then there are some relevant early exits
+        if let Some(trait_did) = trait_did {
+            let trait_did = trait_did.to_def_id();
+            // Never add a default supertrait to `PointeeSized`.
+            if trait_did == pointee_sized_did {
+                return;
+            }
+            // Don't add default sizedness supertraits to auto traits because it isn't possible to
+            // relax an automatically added supertrait on the defn itself.
+            if tcx.trait_is_auto(trait_did) {
+                return;
+            }
+        } else {
+            // Report invalid unbounds on sizedness-bounded generic parameters.
+            let unbounds = collect_unbounds(hir_bounds, self_ty_where_predicates);
+            self.check_and_report_invalid_unbounds_on_param(unbounds);
+        }
+
+        let collected = collect_sizedness_bounds(tcx, hir_bounds, self_ty_where_predicates, span);
+        if (collected.sized.maybe || collected.sized.negative)
+            && !collected.sized.positive
+            && !collected.meta_sized.any()
+            && !collected.pointee_sized.any()
+        {
+            // `?Sized` is equivalent to `MetaSized` (but only add the bound if there aren't any
+            // other explicit ones) - this can happen for trait aliases as well as bounds.
+            add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
+        } else if !collected.any() {
+            if trait_did.is_some() {
+                // If there are no explicit sizedness bounds on a trait then add a default
+                // `MetaSized` supertrait.
+                add_trait_bound(tcx, bounds, self_ty, meta_sized_did, span);
+            } else {
+                // If there are no explicit sizedness bounds on a parameter then add a default
+                // `Sized` bound.
+                let sized_did = tcx.require_lang_item(LangItem::Sized, span);
+                add_trait_bound(tcx, bounds, self_ty, sized_did, span);
+            }
+        }
     }
 
     /// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds
@@ -146,13 +336,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         if !self.requires_default_supertraits(trait_bounds, trait_generics) {
             let self_ty_where_predicates = (parent, trait_item.generics.predicates);
-            self.add_default_traits_with_filter(
+            self.add_default_traits(
                 bounds,
                 tcx.types.self_param,
                 &[],
                 Some(self_ty_where_predicates),
                 trait_item.span,
-                |tr| tr != hir::LangItem::Sized,
             );
         }
     }
@@ -174,41 +363,37 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias));
         if self.requires_default_supertraits(hir_bounds, hir_generics) {
             let self_ty_where_predicates = (trait_def_id, hir_generics.predicates);
-            self.add_default_traits_with_filter(
+            self.add_default_traits(
                 bounds,
                 self.tcx().types.self_param,
                 hir_bounds,
                 Some(self_ty_where_predicates),
                 span,
-                |default_trait| default_trait != hir::LangItem::Sized,
             );
         }
     }
 
-    pub(crate) fn add_default_traits_with_filter(
+    pub(crate) fn add_default_traits(
         &self,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
         self_ty: Ty<'tcx>,
         hir_bounds: &[hir::GenericBound<'tcx>],
         self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
         span: Span,
-        f: impl Fn(hir::LangItem) -> bool,
     ) {
-        self.tcx().default_traits().iter().filter(|&&default_trait| f(default_trait)).for_each(
-            |default_trait| {
-                self.add_default_trait(
-                    *default_trait,
-                    bounds,
-                    self_ty,
-                    hir_bounds,
-                    self_ty_where_predicates,
-                    span,
-                );
-            },
-        );
+        self.tcx().default_traits().iter().for_each(|default_trait| {
+            self.add_default_trait(
+                *default_trait,
+                bounds,
+                self_ty,
+                hir_bounds,
+                self_ty_where_predicates,
+                span,
+            );
+        });
     }
 
-    /// Add a `Sized` or `experimental_default_bounds` bounds to the `bounds` if appropriate.
+    /// Add a `experimental_default_bounds` bound to the `bounds` if appropriate.
     ///
     /// Doesn't add the bound if the HIR bounds contain any of `Trait`, `?Trait` or `!Trait`.
     pub(crate) fn add_default_trait(
@@ -220,7 +405,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
         span: Span,
     ) {
-        let trait_id = self.tcx().lang_items().get(trait_);
+        let tcx = self.tcx();
+        let trait_id = tcx.lang_items().get(trait_);
         if let Some(trait_id) = trait_id
             && self.do_not_provide_default_trait_bound(
                 trait_id,
@@ -228,11 +414,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 self_ty_where_predicates,
             )
         {
-            // There was no `?Trait` or `!Trait` bound;
-            // add `Trait` if it's available.
-            let trait_ref = ty::TraitRef::new(self.tcx(), trait_id, [self_ty]);
-            // Preferable to put this obligation first, since we report better errors for sized ambiguity.
-            bounds.insert(0, (trait_ref.upcast(self.tcx()), span));
+            add_trait_bound(tcx, bounds, self_ty, trait_id, span);
         }
     }
 
@@ -242,90 +424,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         hir_bounds: &'a [hir::GenericBound<'tcx>],
         self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
     ) -> bool {
-        let tcx = self.tcx();
-        let mut seen_negative_bound = false;
-        let mut seen_positive_bound = false;
-
-        // Try to find an unbound in bounds.
-        let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
-        let mut search_bounds = |hir_bounds: &'a [hir::GenericBound<'tcx>]| {
-            for hir_bound in hir_bounds {
-                let hir::GenericBound::Trait(ptr) = hir_bound else {
-                    continue;
-                };
-                match ptr.modifiers.polarity {
-                    hir::BoundPolarity::Maybe(_) => unbounds.push(ptr),
-                    hir::BoundPolarity::Negative(_) => {
-                        if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) {
-                            seen_negative_bound = true;
-                        }
-                    }
-                    hir::BoundPolarity::Positive => {
-                        if ptr.trait_ref.path.res == Res::Def(DefKind::Trait, trait_def_id) {
-                            seen_positive_bound = true;
-                        }
-                    }
-                }
-            }
-        };
-        search_bounds(hir_bounds);
-        if let Some((self_ty, where_clause)) = self_ty_where_predicates {
-            for clause in where_clause {
-                if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind
-                    && pred.is_param_bound(self_ty.to_def_id())
-                {
-                    search_bounds(pred.bounds);
-                }
-            }
-        }
-
-        let mut unique_bounds = FxIndexSet::default();
-        let mut seen_repeat = false;
-        for unbound in &unbounds {
-            if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res {
-                seen_repeat |= !unique_bounds.insert(unbound_def_id);
-            }
-        }
-        if unbounds.len() > 1 {
-            let err = errors::MultipleRelaxedDefaultBounds {
-                spans: unbounds.iter().map(|ptr| ptr.span).collect(),
-            };
-            if seen_repeat {
-                self.dcx().emit_err(err);
-            } else if !tcx.features().more_maybe_bounds() {
-                self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit();
-            };
-        }
-
-        let mut seen_unbound = false;
-        for unbound in unbounds {
-            let unbound_def_id = unbound.trait_ref.trait_def_id();
-            if unbound_def_id == Some(trait_def_id) {
-                seen_unbound = true;
-            }
-            let emit_relax_err = || {
-                let unbound_traits = match tcx.sess.opts.unstable_opts.experimental_default_bounds {
-                    true => "`?Sized` and `experimental_default_bounds`",
-                    false => "`?Sized`",
-                };
-                // There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`.
-                self.dcx().span_err(
-                    unbound.span,
-                    format!(
-                        "relaxing a default bound only does something for {}; \
-                    all other traits are not bound by default",
-                        unbound_traits
-                    ),
-                );
-            };
-            match unbound_def_id {
-                Some(def_id) if !tcx.is_default_trait(def_id) => emit_relax_err(),
-                None => emit_relax_err(),
-                _ => {}
-            }
-        }
-
-        !(seen_unbound || seen_negative_bound || seen_positive_bound)
+        let collected = collect_bounds(hir_bounds, self_ty_where_predicates, trait_def_id);
+        !collected.any()
     }
 
     /// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any.
@@ -361,6 +461,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         'tcx: 'hir,
     {
         for hir_bound in hir_bounds {
+            if self.should_skip_sizedness_bound(hir_bound) {
+                continue;
+            }
+
             // In order to avoid cycles, when we're lowering `SelfTraitThatDefines`,
             // we skip over any traits that don't define the given associated type.
             if let PredicateFilter::SelfTraitThatDefines(assoc_ident) = predicate_filter {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
index c7cdf1d5bd2..05465b47a26 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
@@ -61,14 +61,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let ast_bounds: Vec<_> =
             hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect();
 
-        self.add_default_traits_with_filter(
-            &mut user_written_bounds,
-            dummy_self,
-            &ast_bounds,
-            None,
-            span,
-            |tr| tr != hir::LangItem::Sized,
-        );
+        self.add_default_traits(&mut user_written_bounds, dummy_self, &ast_bounds, None, span);
 
         let (elaborated_trait_bounds, elaborated_projection_bounds) =
             traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied());
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 45fee0fa402..1cda6dff21e 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -8,7 +8,7 @@ use rustc_errors::{
 };
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::{self as hir, HirId};
+use rustc_hir::{self as hir, HirId, LangItem, PolyTraitRef};
 use rustc_middle::bug;
 use rustc_middle::ty::fast_reject::{TreatParams, simplify_type};
 use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@@ -34,6 +34,57 @@ use crate::fluent_generated as fluent;
 use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
+    /// Check for multiple relaxed default bounds and relaxed bounds of non-sizedness traits.
+    pub(crate) fn check_and_report_invalid_unbounds_on_param(
+        &self,
+        unbounds: SmallVec<[&PolyTraitRef<'_>; 1]>,
+    ) {
+        let tcx = self.tcx();
+
+        let sized_did = tcx.require_lang_item(LangItem::Sized, DUMMY_SP);
+
+        let mut unique_bounds = FxIndexSet::default();
+        let mut seen_repeat = false;
+        for unbound in &unbounds {
+            if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res {
+                seen_repeat |= !unique_bounds.insert(unbound_def_id);
+            }
+        }
+
+        if unbounds.len() > 1 {
+            let err = errors::MultipleRelaxedDefaultBounds {
+                spans: unbounds.iter().map(|ptr| ptr.span).collect(),
+            };
+
+            if seen_repeat {
+                tcx.dcx().emit_err(err);
+            } else if !tcx.features().more_maybe_bounds() {
+                tcx.sess.create_feature_err(err, sym::more_maybe_bounds).emit();
+            };
+        }
+
+        for unbound in unbounds {
+            if let Res::Def(DefKind::Trait, did) = unbound.trait_ref.path.res
+                && ((did == sized_did) || tcx.is_default_trait(did))
+            {
+                continue;
+            }
+
+            let unbound_traits = match tcx.sess.opts.unstable_opts.experimental_default_bounds {
+                true => "`?Sized` and `experimental_default_bounds`",
+                false => "`?Sized`",
+            };
+            self.dcx().span_err(
+                unbound.span,
+                format!(
+                    "relaxing a default bound only does something for {}; all other traits are \
+                     not bound by default",
+                    unbound_traits
+                ),
+            );
+        }
+    }
+
     /// On missing type parameters, emit an E0393 error and provide a structured suggestion using
     /// the type parameter's name as a placeholder.
     pub(crate) fn report_missing_type_params(
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index a43449a8f99..2df19cb21d5 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -22,8 +22,9 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferResult};
 use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 use rustc_middle::ty::{
-    self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, Ty, TyCtxt,
-    TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy,
+    self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity,
+    SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs,
+    UserSelfTy,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
@@ -439,7 +440,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 || {},
             );
             // Sized types have static alignment, and so do slices.
-            if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) {
+            if tail.has_trivial_sizedness(self.tcx, SizedTraitKind::Sized)
+                || matches!(tail.kind(), ty::Slice(..))
+            {
                 // Nothing else is required here.
             } else {
                 // We can't be sure, let's required full `Sized`.
diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
index dea7c8ac708..3cc55eaa0f2 100644
--- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
+++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
@@ -46,7 +46,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
                 .tcx
                 .explicit_super_predicates_of(def_id)
                 .iter_identity_copied()
-                .filter_map(|(pred, _)| pred.as_trait_clause());
+                .filter_map(|(pred, _)| pred.as_trait_clause())
+                .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized));
             if direct_super_traits_iter.count() > 1 {
                 cx.emit_span_lint(
                     MULTIPLE_SUPERTRAIT_UPCASTABLE,
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index 667361b3ca0..6cb1d8c5fc4 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -28,6 +28,7 @@
 #![allow(internal_features)]
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
+#![cfg_attr(not(bootstrap), feature(sized_hierarchy))]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
 #![feature(allocator_api)]
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index 9ed1f10455a..4d914c42cfc 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -328,6 +328,14 @@ impl Key for (DefId, SimplifiedType) {
     }
 }
 
+impl Key for (DefId, ty::SizedTraitKind) {
+    type Cache<V> = DefaultCache<Self, V>;
+
+    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
+        self.0.default_span(tcx)
+    }
+}
+
 impl<'tcx> Key for GenericArgsRef<'tcx> {
     type Cache<V> = DefaultCache<Self, V>;
 
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 63312eff490..930d9fba433 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -137,8 +137,8 @@ use crate::ty::layout::ValidityRequirement;
 use crate::ty::print::{PrintTraitRefExt, describe_as_module};
 use crate::ty::util::AlwaysRequiresDrop;
 use crate::ty::{
-    self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, Ty, TyCtxt,
-    TyCtxtFeed,
+    self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, SizedTraitKind, Ty,
+    TyCtxt, TyCtxtFeed,
 };
 use crate::{dep_graph, mir, thir};
 
@@ -910,9 +910,10 @@ rustc_queries! {
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
     }
-
-    query adt_sized_constraint(key: DefId) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
-        desc { |tcx| "computing the `Sized` constraint for `{}`", tcx.def_path_str(key) }
+    query adt_sizedness_constraint(
+        key: (DefId, SizedTraitKind)
+    ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
+        desc { |tcx| "computing the sizedness constraint for `{}`", tcx.def_path_str(key.0) }
     }
 
     query adt_dtorck_constraint(
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index d92b4f9c06b..6d5a3abf665 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -229,8 +229,12 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
         )
     }
 
-    fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
-        self.sized_constraint(tcx)
+    fn sizedness_constraint(
+        self,
+        tcx: TyCtxt<'tcx>,
+        sizedness: ty::SizedTraitKind,
+    ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
+        self.sizedness_constraint(tcx, sizedness)
     }
 
     fn is_fundamental(self) -> bool {
@@ -634,10 +638,15 @@ impl<'tcx> AdtDef<'tcx> {
         tcx.adt_async_destructor(self.did())
     }
 
-    /// Returns a type such that `Self: Sized` if and only if that type is `Sized`,
-    /// or `None` if the type is always sized.
-    pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
-        if self.is_struct() { tcx.adt_sized_constraint(self.did()) } else { None }
+    /// If this ADT is a struct, returns a type such that `Self: {Meta,Pointee,}Sized` if and only
+    /// if that type is `{Meta,Pointee,}Sized`, or `None` if this ADT is always
+    /// `{Meta,Pointee,}Sized`.
+    pub fn sizedness_constraint(
+        self,
+        tcx: TyCtxt<'tcx>,
+        sizedness: ty::SizedTraitKind,
+    ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
+        if self.is_struct() { tcx.adt_sizedness_constraint((self.did(), sizedness)) } else { None }
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index e0f70737add..3d7965f6cfe 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -13,7 +13,7 @@ use std::marker::DiscriminantKind;
 use rustc_abi::{FieldIdx, VariantIdx};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::LocalDefId;
-use rustc_serialize::{Decodable, Encodable};
+use rustc_serialize::{Decodable, Encodable, PointeeSized};
 use rustc_span::source_map::Spanned;
 use rustc_span::{Span, SpanDecoder, SpanEncoder};
 
@@ -96,7 +96,7 @@ impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for ty::Predicate
 ///
 /// `Decodable` can still be implemented in cases where `Decodable` is required
 /// by a trait bound.
-pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>> {
+pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>>: PointeeSized {
     fn decode(d: &mut D) -> &'tcx Self;
 }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index fe4dd8d080b..f1395c242f2 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -43,6 +43,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_query_system::cache::WithDepNode;
 use rustc_query_system::dep_graph::DepNodeIndex;
 use rustc_query_system::ich::StableHashingContext;
+use rustc_serialize::PointeeSized;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
 use rustc_session::config::CrateType;
 use rustc_session::cstore::{CrateStoreDyn, Untracked};
@@ -774,7 +775,9 @@ bidirectional_lang_item_map! {
     FutureOutput,
     Iterator,
     Metadata,
+    MetaSized,
     Option,
+    PointeeSized,
     PointeeTrait,
     Poll,
     Sized,
@@ -1616,16 +1619,17 @@ impl<'tcx> TyCtxt<'tcx> {
         self.reserve_and_set_memory_dedup(alloc, salt)
     }
 
+    /// Traits added on all bounds by default, excluding `Sized` which is treated separately.
     pub fn default_traits(self) -> &'static [rustc_hir::LangItem] {
-        match self.sess.opts.unstable_opts.experimental_default_bounds {
-            true => &[
-                LangItem::Sized,
+        if self.sess.opts.unstable_opts.experimental_default_bounds {
+            &[
                 LangItem::DefaultTrait1,
                 LangItem::DefaultTrait2,
                 LangItem::DefaultTrait3,
                 LangItem::DefaultTrait4,
-            ],
-            false => &[LangItem::Sized],
+            ]
+        } else {
+            &[]
         }
     }
 
@@ -2538,17 +2542,17 @@ impl<'tcx> TyCtxt<'tcx> {
 // this type just holds a pointer to it, but it still effectively owns it. It
 // impls `Borrow` so that it can be looked up using the original
 // (non-arena-memory-owning) types.
-struct InternedInSet<'tcx, T: ?Sized>(&'tcx T);
+struct InternedInSet<'tcx, T: ?Sized + PointeeSized>(&'tcx T);
 
-impl<'tcx, T: 'tcx + ?Sized> Clone for InternedInSet<'tcx, T> {
+impl<'tcx, T: 'tcx + ?Sized + PointeeSized> Clone for InternedInSet<'tcx, T> {
     fn clone(&self) -> Self {
         InternedInSet(self.0)
     }
 }
 
-impl<'tcx, T: 'tcx + ?Sized> Copy for InternedInSet<'tcx, T> {}
+impl<'tcx, T: 'tcx + ?Sized + PointeeSized> Copy for InternedInSet<'tcx, T> {}
 
-impl<'tcx, T: 'tcx + ?Sized> IntoPointer for InternedInSet<'tcx, T> {
+impl<'tcx, T: 'tcx + ?Sized + PointeeSized> IntoPointer for InternedInSet<'tcx, T> {
     fn into_pointer(&self) -> *const () {
         self.0 as *const _ as *const ()
     }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 0402d098822..97408e31854 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -60,6 +60,7 @@ pub use rustc_type_ir::fast_reject::DeepRejectCtxt;
 )]
 use rustc_type_ir::inherent;
 pub use rustc_type_ir::relate::VarianceDiagInfo;
+pub use rustc_type_ir::solve::SizedTraitKind;
 pub use rustc_type_ir::*;
 #[allow(hidden_glob_reexports, unused_imports)]
 use rustc_type_ir::{InferCtxtLike, Interner};
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 673a89a8134..c10277c75a7 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1069,24 +1069,35 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
 
         let mut traits = FxIndexMap::default();
         let mut fn_traits = FxIndexMap::default();
+        let mut lifetimes = SmallVec::<[ty::Region<'tcx>; 1]>::new();
+
         let mut has_sized_bound = false;
         let mut has_negative_sized_bound = false;
-        let mut lifetimes = SmallVec::<[ty::Region<'tcx>; 1]>::new();
+        let mut has_meta_sized_bound = false;
 
         for (predicate, _) in bounds.iter_instantiated_copied(tcx, args) {
             let bound_predicate = predicate.kind();
 
             match bound_predicate.skip_binder() {
                 ty::ClauseKind::Trait(pred) => {
-                    // Don't print `+ Sized`, but rather `+ ?Sized` if absent.
-                    if tcx.is_lang_item(pred.def_id(), LangItem::Sized) {
-                        match pred.polarity {
+                    // With `feature(sized_hierarchy)`, don't print `?Sized` as an alias for
+                    // `MetaSized`, and skip sizedness bounds to be added at the end.
+                    match tcx.as_lang_item(pred.def_id()) {
+                        Some(LangItem::Sized) => match pred.polarity {
                             ty::PredicatePolarity::Positive => {
                                 has_sized_bound = true;
                                 continue;
                             }
                             ty::PredicatePolarity::Negative => has_negative_sized_bound = true,
+                        },
+                        Some(LangItem::MetaSized) => {
+                            has_meta_sized_bound = true;
+                            continue;
+                        }
+                        Some(LangItem::PointeeSized) => {
+                            bug!("`PointeeSized` is removed during lowering");
                         }
+                        _ => (),
                     }
 
                     self.insert_trait_and_projection(
@@ -1255,8 +1266,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             })?;
         }
 
+        let using_sized_hierarchy = self.tcx().features().sized_hierarchy();
         let add_sized = has_sized_bound && (first || has_negative_sized_bound);
-        let add_maybe_sized = !has_sized_bound && !has_negative_sized_bound;
+        let add_maybe_sized =
+            has_meta_sized_bound && !has_negative_sized_bound && !using_sized_hierarchy;
+        // Set `has_pointee_sized_bound` if there were no `Sized` or `MetaSized` bounds.
+        let has_pointee_sized_bound =
+            !has_sized_bound && !has_meta_sized_bound && !has_negative_sized_bound;
         if add_sized || add_maybe_sized {
             if !first {
                 write!(self, " + ")?;
@@ -1265,6 +1281,16 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                 write!(self, "?")?;
             }
             write!(self, "Sized")?;
+        } else if has_meta_sized_bound && using_sized_hierarchy {
+            if !first {
+                write!(self, " + ")?;
+            }
+            write!(self, "MetaSized")?;
+        } else if has_pointee_sized_bound && using_sized_hierarchy {
+            if !first {
+                write!(self, " + ")?;
+            }
+            write!(self, "PointeeSized")?;
         }
 
         if !with_forced_trimmed_paths() {
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index cbf545c01c5..58829f72a72 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -15,6 +15,7 @@ use rustc_hir::def_id::DefId;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension};
 use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 use rustc_type_ir::TyKind::*;
+use rustc_type_ir::solve::SizedTraitKind;
 use rustc_type_ir::walk::TypeWalker;
 use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate};
 use tracing::instrument;
@@ -1677,7 +1678,7 @@ impl<'tcx> Ty<'tcx> {
         let Some(pointee_ty) = self.builtin_deref(true) else {
             bug!("Type {self:?} is not a pointer or reference type")
         };
-        if pointee_ty.is_trivially_sized(tcx) {
+        if pointee_ty.has_trivial_sizedness(tcx, SizedTraitKind::Sized) {
             tcx.types.unit
         } else {
             match pointee_ty.ptr_metadata_ty_or_tail(tcx, |x| x) {
@@ -1778,17 +1779,17 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
-    /// Fast path helper for testing if a type is `Sized`.
+    /// Fast path helper for testing if a type is `Sized` or `MetaSized`.
     ///
-    /// Returning true means the type is known to implement `Sized`. Returning `false` means
-    /// nothing -- could be sized, might not be.
+    /// Returning true means the type is known to implement the sizedness trait. Returning `false`
+    /// means nothing -- could be sized, might not be.
     ///
     /// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized`
     /// because we could be in a type environment with a bound such as `[_]: Copy`. A function with
     /// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck.
     /// This is why this method doesn't return `Option<bool>`.
     #[instrument(skip(tcx), level = "debug")]
-    pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool {
+    pub fn has_trivial_sizedness(self, tcx: TyCtxt<'tcx>, sizedness: SizedTraitKind) -> bool {
         match self.kind() {
             ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
             | ty::Uint(_)
@@ -1811,13 +1812,20 @@ impl<'tcx> Ty<'tcx> {
             | ty::Error(_)
             | ty::Dynamic(_, _, ty::DynStar) => true,
 
-            ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
+            ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) => match sizedness {
+                SizedTraitKind::Sized => false,
+                SizedTraitKind::MetaSized => true,
+            },
+
+            ty::Foreign(..) => match sizedness {
+                SizedTraitKind::Sized | SizedTraitKind::MetaSized => false,
+            },
 
-            ty::Tuple(tys) => tys.last().is_none_or(|ty| ty.is_trivially_sized(tcx)),
+            ty::Tuple(tys) => tys.last().is_none_or(|ty| ty.has_trivial_sizedness(tcx, sizedness)),
 
             ty::Adt(def, args) => def
-                .sized_constraint(tcx)
-                .is_none_or(|ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)),
+                .sizedness_constraint(tcx, sizedness)
+                .is_none_or(|ty| ty.instantiate(tcx, args).has_trivial_sizedness(tcx, sizedness)),
 
             ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false,
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 461d92f8006..51f57e71ce9 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -16,6 +16,7 @@ use rustc_index::bit_set::GrowableBitSet;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension};
 use rustc_session::Limit;
 use rustc_span::sym;
+use rustc_type_ir::solve::SizedTraitKind;
 use smallvec::{SmallVec, smallvec};
 use tracing::{debug, instrument};
 
@@ -1132,7 +1133,8 @@ impl<'tcx> Ty<'tcx> {
     /// strange rules like `<T as Foo<'static>>::Bar: Sized` that
     /// actually carry lifetime requirements.
     pub fn is_sized(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
-        self.is_trivially_sized(tcx) || tcx.is_sized_raw(typing_env.as_query_input(self))
+        self.has_trivial_sizedness(tcx, SizedTraitKind::Sized)
+            || tcx.is_sized_raw(typing_env.as_query_input(self))
     }
 
     /// Checks whether values of this type `T` implement the `Freeze`
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 0c267feefbe..a300558c0c9 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -8,6 +8,7 @@ use std::ops::ControlFlow;
 use derive_where::derive_where;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::solve::SizedTraitKind;
 use rustc_type_ir::{
     self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _,
     TypeVisitor, TypingMode, Upcast as _, elaborate,
@@ -203,13 +204,15 @@ where
         goal: Goal<I, Self>,
     ) -> Result<Candidate<I>, NoSolution>;
 
-    /// A type is `Sized` if its tail component is `Sized`.
+    /// A type is `Sized` if its tail component is `Sized` and a type is `MetaSized` if its tail
+    /// component is `MetaSized`.
     ///
     /// These components are given by built-in rules from
-    /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`].
-    fn consider_builtin_sized_candidate(
+    /// [`structural_traits::instantiate_constituent_tys_for_sizedness_trait`].
+    fn consider_builtin_sizedness_candidates(
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
+        sizedness: SizedTraitKind,
     ) -> Result<Candidate<I>, NoSolution>;
 
     /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`.
@@ -466,7 +469,15 @@ where
             G::consider_trait_alias_candidate(self, goal)
         } else {
             match cx.as_lang_item(trait_def_id) {
-                Some(TraitSolverLangItem::Sized) => G::consider_builtin_sized_candidate(self, goal),
+                Some(TraitSolverLangItem::Sized) => {
+                    G::consider_builtin_sizedness_candidates(self, goal, SizedTraitKind::Sized)
+                }
+                Some(TraitSolverLangItem::MetaSized) => {
+                    G::consider_builtin_sizedness_candidates(self, goal, SizedTraitKind::MetaSized)
+                }
+                Some(TraitSolverLangItem::PointeeSized) => {
+                    unreachable!("`PointeeSized` is removed during lowering");
+                }
                 Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => {
                     G::consider_builtin_copy_clone_candidate(self, goal)
                 }
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index 4a2a7761f7f..f39d3226009 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -5,6 +5,7 @@ use derive_where::derive_where;
 use rustc_type_ir::data_structures::HashMap;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::solve::SizedTraitKind;
 use rustc_type_ir::solve::inspect::ProbeKind;
 use rustc_type_ir::{
     self as ty, FallibleTypeFolder, Interner, Movability, Mutability, TypeFoldable,
@@ -104,8 +105,9 @@ where
 }
 
 #[instrument(level = "trace", skip(ecx), ret)]
-pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<D, I>(
+pub(in crate::solve) fn instantiate_constituent_tys_for_sizedness_trait<D, I>(
     ecx: &EvalCtxt<'_, D>,
+    sizedness: SizedTraitKind,
     ty: I::Ty,
 ) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>
 where
@@ -113,8 +115,9 @@ where
     I: Interner,
 {
     match ty.kind() {
-        // impl Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, !
-        // impl Sized for Coroutine, CoroutineWitness, Closure, CoroutineClosure
+        // impl {Meta,}Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char
+        // impl {Meta,}Sized for &mut? T, [T; N], dyn* Trait, !, Coroutine, CoroutineWitness
+        // impl {Meta,}Sized for Closure, CoroutineClosure
         ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
         | ty::Uint(_)
         | ty::Int(_)
@@ -135,13 +138,16 @@ where
         | ty::Dynamic(_, _, ty::DynStar)
         | ty::Error(_) => Ok(ty::Binder::dummy(vec![])),
 
-        ty::Str
-        | ty::Slice(_)
-        | ty::Dynamic(..)
-        | ty::Foreign(..)
-        | ty::Alias(..)
-        | ty::Param(_)
-        | ty::Placeholder(..) => Err(NoSolution),
+        // impl {Meta,}Sized for str, [T], dyn Trait
+        ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness {
+            SizedTraitKind::Sized => Err(NoSolution),
+            SizedTraitKind::MetaSized => Ok(ty::Binder::dummy(vec![])),
+        },
+
+        // impl {} for extern type
+        ty::Foreign(..) => Err(NoSolution),
+
+        ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => Err(NoSolution),
 
         ty::Bound(..)
         | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
@@ -150,22 +156,27 @@ where
 
         ty::UnsafeBinder(bound_ty) => Ok(bound_ty.map_bound(|ty| vec![ty])),
 
-        // impl Sized for ()
-        // impl Sized for (T1, T2, .., Tn) where Tn: Sized if n >= 1
+        // impl {Meta,}Sized for ()
+        // impl {Meta,}Sized for (T1, T2, .., Tn) where Tn: {Meta,}Sized if n >= 1
         ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.last().map_or_else(Vec::new, |ty| vec![ty]))),
 
-        // impl Sized for Adt<Args...> where sized_constraint(Adt)<Args...>: Sized
-        //   `sized_constraint(Adt)` is the deepest struct trail that can be determined
-        //   by the definition of `Adt`, independent of the generic args.
-        // impl Sized for Adt<Args...> if sized_constraint(Adt) == None
-        //   As a performance optimization, `sized_constraint(Adt)` can return `None`
-        //   if the ADTs definition implies that it is sized by for all possible args.
+        // impl {Meta,}Sized for Adt<Args...>
+        //   where {meta,pointee,}sized_constraint(Adt)<Args...>: {Meta,}Sized
+        //
+        //   `{meta,pointee,}sized_constraint(Adt)` is the deepest struct trail that can be
+        //   determined by the definition of `Adt`, independent of the generic args.
+        //
+        // impl {Meta,}Sized for Adt<Args...>
+        //   if {meta,pointee,}sized_constraint(Adt) == None
+        //
+        //   As a performance optimization, `{meta,pointee,}sized_constraint(Adt)` can return `None`
+        //   if the ADTs definition implies that it is {meta,}sized by for all possible args.
         //   In this case, the builtin impl will have no nested subgoals. This is a
-        //   "best effort" optimization and `sized_constraint` may return `Some`, even
-        //   if the ADT is sized for all possible args.
+        //   "best effort" optimization and `{meta,pointee,}sized_constraint` may return `Some`,
+        //   even if the ADT is {meta,pointee,}sized for all possible args.
         ty::Adt(def, args) => {
-            if let Some(sized_crit) = def.sized_constraint(ecx.cx()) {
-                Ok(ty::Binder::dummy(vec![sized_crit.instantiate(ecx.cx(), args)]))
+            if let Some(crit) = def.sizedness_constraint(ecx.cx(), sizedness) {
+                Ok(ty::Binder::dummy(vec![crit.instantiate(ecx.cx(), args)]))
             } else {
                 Ok(ty::Binder::dummy(vec![]))
             }
diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
index 8413c2abbb9..1690c908d12 100644
--- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
@@ -4,6 +4,7 @@
 use rustc_type_ir::fast_reject::DeepRejectCtxt;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::solve::SizedTraitKind;
 use rustc_type_ir::solve::inspect::ProbeKind;
 use rustc_type_ir::{self as ty, Interner, elaborate};
 use tracing::instrument;
@@ -198,11 +199,12 @@ where
         unreachable!("trait aliases are never const")
     }
 
-    fn consider_builtin_sized_candidate(
+    fn consider_builtin_sizedness_candidates(
         _ecx: &mut EvalCtxt<'_, D>,
         _goal: Goal<I, Self>,
+        _sizedness: SizedTraitKind,
     ) -> Result<Candidate<I>, NoSolution> {
-        unreachable!("Sized is never const")
+        unreachable!("Sized/MetaSized is never const")
     }
 
     fn consider_builtin_copy_clone_candidate(
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index 2fddc0044cb..d51c87fe68e 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -6,6 +6,7 @@ mod opaque_types;
 use rustc_type_ir::fast_reject::DeepRejectCtxt;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::solve::SizedTraitKind;
 use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _};
 use tracing::instrument;
 
@@ -413,11 +414,12 @@ where
         panic!("trait aliases do not have associated types: {:?}", goal);
     }
 
-    fn consider_builtin_sized_candidate(
+    fn consider_builtin_sizedness_candidates(
         _ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
+        _sizedness: SizedTraitKind,
     ) -> Result<Candidate<I>, NoSolution> {
-        panic!("`Sized` does not have an associated type: {:?}", goal);
+        panic!("`Sized`/`MetaSized` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_copy_clone_candidate(
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 110c67a8e21..8ee116b090d 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -4,9 +4,9 @@ use rustc_type_ir::data_structures::IndexSet;
 use rustc_type_ir::fast_reject::DeepRejectCtxt;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
-use rustc_type_ir::solve::CanonicalResponse;
+use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind};
 use rustc_type_ir::{
-    self as ty, Interner, Movability, TraitPredicate, TypeVisitableExt as _, TypingMode,
+    self as ty, Interner, Movability, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode,
     Upcast as _, elaborate,
 };
 use tracing::{debug, instrument, trace};
@@ -131,9 +131,11 @@ where
         assumption: I::Clause,
     ) -> Result<(), NoSolution> {
         if let Some(trait_clause) = assumption.as_trait_clause() {
-            if trait_clause.def_id() == goal.predicate.def_id()
-                && trait_clause.polarity() == goal.predicate.polarity
-            {
+            if trait_clause.polarity() != goal.predicate.polarity {
+                return Err(NoSolution);
+            }
+
+            if trait_clause.def_id() == goal.predicate.def_id() {
                 if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
                     goal.predicate.trait_ref.args,
                     trait_clause.skip_binder().trait_ref.args,
@@ -141,6 +143,17 @@ where
                     return Ok(());
                 }
             }
+
+            // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
+            // check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
+            // are syntactic sugar for a lack of bounds so don't need this.
+            if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized)
+                && ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized)
+            {
+                let meta_sized_clause =
+                    trait_predicate_with_def_id(ecx.cx(), trait_clause, goal.predicate.def_id());
+                return Self::fast_reject_assumption(ecx, goal, meta_sized_clause);
+            }
         }
 
         Err(NoSolution)
@@ -154,6 +167,17 @@ where
     ) -> QueryResult<I> {
         let trait_clause = assumption.as_trait_clause().unwrap();
 
+        // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
+        // check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
+        // are syntactic sugar for a lack of bounds so don't need this.
+        if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized)
+            && ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized)
+        {
+            let meta_sized_clause =
+                trait_predicate_with_def_id(ecx.cx(), trait_clause, goal.predicate.def_id());
+            return Self::match_assumption(ecx, goal, meta_sized_clause, then);
+        }
+
         let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
         ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
 
@@ -245,9 +269,10 @@ where
         })
     }
 
-    fn consider_builtin_sized_candidate(
+    fn consider_builtin_sizedness_candidates(
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
+        sizedness: SizedTraitKind,
     ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
@@ -256,7 +281,11 @@ where
         ecx.probe_and_evaluate_goal_for_constituent_tys(
             CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial),
             goal,
-            structural_traits::instantiate_constituent_tys_for_sized_trait,
+            |ecx, ty| {
+                structural_traits::instantiate_constituent_tys_for_sizedness_trait(
+                    ecx, sizedness, ty,
+                )
+            },
         )
     }
 
@@ -812,6 +841,25 @@ where
     }
 }
 
+/// Small helper function to change the `def_id` of a trait predicate - this is not normally
+/// something that you want to do, as different traits will require different args and so making
+/// it easy to change the trait is something of a footgun, but it is useful in the narrow
+/// circumstance of changing from `MetaSized` to `Sized`, which happens as part of the lazy
+/// elaboration of sizedness candidates.
+#[inline(always)]
+fn trait_predicate_with_def_id<I: Interner>(
+    cx: I,
+    clause: ty::Binder<I, ty::TraitPredicate<I>>,
+    did: I::DefId,
+) -> I::Clause {
+    clause
+        .map_bound(|c| TraitPredicate {
+            trait_ref: TraitRef::new_from_args(cx, did, c.trait_ref.args),
+            polarity: c.polarity,
+        })
+        .upcast(cx)
+}
+
 impl<D, I> EvalCtxt<'_, D>
 where
     D: SolverDelegate<Interner = I>,
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index 34be35e36ac..656ecfcab36 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -3,6 +3,7 @@
 // tidy-alphabetical-start
 #![allow(internal_features)]
 #![allow(rustc::internal)]
+#![cfg_attr(not(bootstrap), feature(sized_hierarchy))]
 #![cfg_attr(test, feature(test))]
 #![doc(
     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
@@ -27,3 +28,19 @@ mod serialize;
 pub mod int_overflow;
 pub mod leb128;
 pub mod opaque;
+
+// This has nothing to do with `rustc_serialize` but it is convenient to define it in one place
+// for the rest of the compiler so that `cfg(bootstrap)` doesn't need to be littered throughout
+// the compiler wherever `PointeeSized` would be used. `rustc_serialize` happens to be the deepest
+// crate in the crate graph which uses `PointeeSized`.
+//
+// When bootstrap bumps, remove both the `cfg(not(bootstrap))` and `cfg(bootstrap)` lines below
+// and just import `std::marker::PointeeSized` whereever this item was used.
+
+#[cfg(not(bootstrap))]
+pub use std::marker::PointeeSized;
+
+#[cfg(bootstrap)]
+pub trait PointeeSized {}
+#[cfg(bootstrap)]
+impl<T: ?Sized> PointeeSized for T {}
diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs
index 1eefd76f92b..8940d10696d 100644
--- a/compiler/rustc_serialize/src/serialize.rs
+++ b/compiler/rustc_serialize/src/serialize.rs
@@ -142,7 +142,7 @@ pub trait Decoder {
 ///   `rustc_metadata::rmeta::Lazy`.
 /// * `TyEncodable` should be used for types that are only serialized in crate
 ///   metadata or the incremental cache. This is most types in `rustc_middle`.
-pub trait Encodable<S: Encoder> {
+pub trait Encodable<S: Encoder>: crate::PointeeSized {
     fn encode(&self, s: &mut S);
 }
 
@@ -198,7 +198,7 @@ direct_serialize_impls! {
     char emit_char read_char
 }
 
-impl<S: Encoder, T: ?Sized> Encodable<S> for &T
+impl<S: Encoder, T: ?Sized + crate::PointeeSized> Encodable<S> for &T
 where
     T: Encodable<S>,
 {
diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs
index 771ff98d58d..63623cfad72 100644
--- a/compiler/rustc_smir/src/lib.rs
+++ b/compiler/rustc_smir/src/lib.rs
@@ -9,6 +9,7 @@
 // tidy-alphabetical-start
 #![allow(internal_features)]
 #![allow(rustc::usage_of_ty_tykind)]
+#![cfg_attr(not(bootstrap), feature(sized_hierarchy))]
 #![doc(
     html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
     test(attr(allow(unused_variables), deny(warnings)))
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index b5003baaf63..64fe181c26d 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -9,6 +9,7 @@
 
 use std::ops::RangeInclusive;
 
+use rustc_data_structures::PointeeSized;
 use rustc_hir::def::DefKind;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::AllocId;
@@ -158,7 +159,7 @@ pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind {
 }
 
 /// Trait used to convert between an internal MIR type to a Stable MIR type.
-pub trait Stable<'cx> {
+pub trait Stable<'cx>: PointeeSized {
     /// The stable representation of the type implementing Stable.
     type T;
     /// Converts an object to the equivalent Stable MIR representation.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index cb9ccf4cc3f..baadff16120 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1355,6 +1355,7 @@ symbols! {
         memtag,
         message,
         meta,
+        meta_sized,
         metadata_type,
         min_const_fn,
         min_const_generics,
@@ -1613,6 +1614,7 @@ symbols! {
         plugin_registrar,
         plugins,
         pointee,
+        pointee_sized,
         pointee_trait,
         pointer,
         pointer_like,
@@ -2016,6 +2018,7 @@ symbols! {
         size_of,
         size_of_val,
         sized,
+        sized_hierarchy,
         skip,
         slice,
         slice_from_raw_parts,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
index 275b580d794..39f115ce0cd 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
@@ -199,7 +199,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 // avoid inundating the user with unnecessary errors, but we now
                 // check upstream for type errors and don't add the obligations to
                 // begin with in those cases.
-                if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized) {
+                if matches!(
+                    self.tcx.as_lang_item(trait_pred.def_id()),
+                    Some(LangItem::Sized | LangItem::MetaSized)
+                ) {
                     match self.tainted_by_errors() {
                         None => {
                             let err = self.emit_inference_failure_err(
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index 78f9287b407..d4cc1ceb280 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::{self as hir, AmbigArg, LangItem};
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_infer::traits::solve::Goal;
 use rustc_infer::traits::{
     DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode,
@@ -160,17 +160,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             })
             .collect();
 
-        // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
-        // with more relevant type information and hide redundant E0282 errors.
-        errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
-                if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) =>
-            {
-                1
+        // Ensure `T: Sized`, `T: MetaSized`, `T: PointeeSized` and `T: WF` obligations come last.
+        // This lets us display diagnostics with more relevant type information and hide redundant
+        // E0282 errors.
+        errors.sort_by_key(|e| {
+            let maybe_sizedness_did = match e.obligation.predicate.kind().skip_binder() {
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred.def_id()),
+                ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => Some(pred.def_id()),
+                _ => None,
+            };
+
+            match e.obligation.predicate.kind().skip_binder() {
+                _ if maybe_sizedness_did == self.tcx.lang_items().sized_trait() => 1,
+                _ if maybe_sizedness_did == self.tcx.lang_items().meta_sized_trait() => 2,
+                _ if maybe_sizedness_did == self.tcx.lang_items().pointee_sized_trait() => 3,
+                ty::PredicateKind::Coerce(_) => 4,
+                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 5,
+                _ => 0,
             }
-            ty::PredicateKind::Coerce(_) => 2,
-            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
-            _ => 0,
         });
 
         for (index, error) in errors.iter().enumerate() {
@@ -327,19 +334,26 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
     let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity();
     let mut w = "impl".to_owned();
 
-    let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id);
+    #[derive(Debug, Default)]
+    struct SizednessFound {
+        sized: bool,
+        meta_sized: bool,
+    }
 
-    // FIXME: Currently only handles ?Sized.
-    //        Needs to support ?Move and ?DynSized when they are implemented.
-    let mut types_without_default_bounds = FxIndexSet::default();
-    let sized_trait = tcx.lang_items().sized_trait();
+    let mut types_with_sizedness_bounds = FxIndexMap::<_, SizednessFound>::default();
+
+    let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id);
 
     let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>();
     if !arg_names.is_empty() {
-        types_without_default_bounds.extend(args.types());
         w.push('<');
         w.push_str(&arg_names.join(", "));
         w.push('>');
+
+        for ty in args.types() {
+            // `PointeeSized` params might have no predicates.
+            types_with_sizedness_bounds.insert(ty, SizednessFound::default());
+        }
     }
 
     write!(
@@ -351,24 +365,47 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
     )
     .unwrap();
 
-    // The predicates will contain default bounds like `T: Sized`. We need to
-    // remove these bounds, and add `T: ?Sized` to any untouched type parameters.
     let predicates = tcx.predicates_of(impl_def_id).predicates;
-    let mut pretty_predicates =
-        Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
+    let mut pretty_predicates = Vec::with_capacity(predicates.len());
+
+    let sized_trait = tcx.lang_items().sized_trait();
+    let meta_sized_trait = tcx.lang_items().meta_sized_trait();
 
     for (p, _) in predicates {
-        if let Some(poly_trait_ref) = p.as_trait_clause() {
-            if Some(poly_trait_ref.def_id()) == sized_trait {
-                // FIXME(#120456) - is `swap_remove` correct?
-                types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder());
+        // Accumulate the sizedness bounds for each self ty.
+        if let Some(trait_clause) = p.as_trait_clause() {
+            let self_ty = trait_clause.self_ty().skip_binder();
+            let sizedness_of = types_with_sizedness_bounds.entry(self_ty).or_default();
+            if Some(trait_clause.def_id()) == sized_trait {
+                sizedness_of.sized = true;
+                continue;
+            } else if Some(trait_clause.def_id()) == meta_sized_trait {
+                sizedness_of.meta_sized = true;
                 continue;
             }
         }
+
         pretty_predicates.push(p.to_string());
     }
 
-    pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized")));
+    for (ty, sizedness) in types_with_sizedness_bounds {
+        if !tcx.features().sized_hierarchy() {
+            if sizedness.sized {
+                // Maybe a default bound, don't write anything.
+            } else {
+                pretty_predicates.push(format!("{ty}: ?Sized"));
+            }
+        } else {
+            if sizedness.sized {
+                // Maybe a default bound, don't write anything.
+                pretty_predicates.push(format!("{ty}: Sized"));
+            } else if sizedness.meta_sized {
+                pretty_predicates.push(format!("{ty}: MetaSized"));
+            } else {
+                pretty_predicates.push(format!("{ty}: PointeeSized"));
+            }
+        }
+    }
 
     if !pretty_predicates.is_empty() {
         write!(w, "\n  where {}", pretty_predicates.join(", ")).unwrap();
@@ -519,7 +556,7 @@ fn attempt_dyn_to_enum_suggestion(
             let Some(impl_type) = tcx.type_of(*impl_id).no_bound_vars() else { return None };
 
             // Obviously unsized impl types won't be usable in an enum.
-            // Note: this doesn't use `Ty::is_trivially_sized` because that function
+            // Note: this doesn't use `Ty::has_trivial_sizedness` because that function
             // defaults to assuming that things are *not* sized, whereas we want to
             // fall back to assuming that things may be sized.
             match impl_type.kind() {
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 69a0c0809b5..b247c2c2968 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -12,7 +12,7 @@ use rustc_infer::traits::solve::Goal;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::Certainty;
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode,
+    self, SizedTraitKind, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode,
 };
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
 
@@ -79,7 +79,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
                     Some(LangItem::Sized)
                         if self
                             .resolve_vars_if_possible(trait_pred.self_ty().skip_binder())
-                            .is_trivially_sized(self.0.tcx) =>
+                            .has_trivial_sizedness(self.0.tcx, SizedTraitKind::Sized) =>
+                    {
+                        return Some(Certainty::Yes);
+                    }
+                    Some(LangItem::MetaSized)
+                        if self
+                            .resolve_vars_if_possible(trait_pred.self_ty().skip_binder())
+                            .has_trivial_sizedness(self.0.tcx, SizedTraitKind::MetaSized) =>
                     {
                         return Some(Certainty::Yes);
                     }
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 93c7dae9c5b..81893cdcc7e 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -462,6 +462,7 @@ fn impl_intersection_has_negative_obligation(
     let param_env = infcx.resolve_vars_if_possible(param_env);
 
     util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args))
+        .elaborate_sized()
         .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env))
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 47d207e8d41..ee30956295a 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -7,15 +7,15 @@
 use std::ops::ControlFlow;
 
 use rustc_errors::FatalError;
-use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, LangItem};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
     self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
     TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
     elaborate,
 };
-use rustc_span::Span;
+use rustc_span::{DUMMY_SP, Span};
 use smallvec::SmallVec;
 use tracing::{debug, instrument};
 
@@ -543,11 +543,11 @@ fn receiver_for_self_ty<'tcx>(
 /// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result in
 /// a new check that `Trait` is dyn-compatible, creating a cycle.
 /// Instead, we emulate a placeholder by introducing a new type parameter `U` such that
-/// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`.
+/// `Self: Unsize<U>` and `U: Trait + MetaSized`, and use `U` in place of `dyn Trait`.
 ///
 /// Written as a chalk-style query:
 /// ```ignore (not-rust)
-/// forall (U: Trait + ?Sized) {
+/// forall (U: Trait + MetaSized) {
 ///     if (Self: Unsize<U>) {
 ///         Receiver: DispatchFromDyn<Receiver[Self => U]>
 ///     }
@@ -567,9 +567,10 @@ fn receiver_is_dispatchable<'tcx>(
 ) -> bool {
     debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty);
 
-    let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait());
-    let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else {
-        debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits");
+    let (Some(unsize_did), Some(dispatch_from_dyn_did)) =
+        (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait())
+    else {
+        debug!("receiver_is_dispatchable: Missing `Unsize` or `DispatchFromDyn` traits");
         return false;
     };
 
@@ -583,7 +584,7 @@ fn receiver_is_dispatchable<'tcx>(
         receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id);
 
     // create a modified param env, with `Self: Unsize<U>` and `U: Trait` (and all of
-    // its supertraits) added to caller bounds. `U: ?Sized` is already implied here.
+    // its supertraits) added to caller bounds. `U: MetaSized` is already implied here.
     let param_env = {
         // N.B. We generally want to emulate the construction of the `unnormalized_param_env`
         // in the param-env query here. The fact that we don't just start with the clauses
@@ -612,6 +613,12 @@ fn receiver_is_dispatchable<'tcx>(
         let trait_predicate = ty::TraitRef::new_from_args(tcx, trait_def_id, args);
         predicates.push(trait_predicate.upcast(tcx));
 
+        let meta_sized_predicate = {
+            let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, DUMMY_SP);
+            ty::TraitRef::new(tcx, meta_sized_did, [unsized_self_ty]).upcast(tcx)
+        };
+        predicates.push(meta_sized_predicate);
+
         normalize_param_env_or_error(
             tcx,
             ty::ParamEnv::new(tcx.mk_clauses(&predicates)),
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 97ecf9702e6..81ce58a93fa 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -14,7 +14,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind};
 use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
 use rustc_middle::ty::fast_reject::DeepRejectCtxt;
-use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode, elaborate};
+use rustc_middle::ty::{self, SizedTraitKind, Ty, TypeVisitableExt, TypingMode, elaborate};
 use rustc_middle::{bug, span_bug};
 use tracing::{debug, instrument, trace};
 
@@ -87,7 +87,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     candidates.vec.push(BuiltinCandidate { has_nested: false });
                 }
                 Some(LangItem::Sized) => {
-                    self.assemble_builtin_sized_candidate(obligation, &mut candidates);
+                    self.assemble_builtin_sized_candidate(
+                        obligation,
+                        &mut candidates,
+                        SizedTraitKind::Sized,
+                    );
+                }
+                Some(LangItem::MetaSized) => {
+                    self.assemble_builtin_sized_candidate(
+                        obligation,
+                        &mut candidates,
+                        SizedTraitKind::MetaSized,
+                    );
+                }
+                Some(LangItem::PointeeSized) => {
+                    bug!("`PointeeSized` is removed during lowering");
                 }
                 Some(LangItem::Unsize) => {
                     self.assemble_candidates_for_unsizing(obligation, &mut candidates);
@@ -201,6 +215,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     }
 
                     selcx.infcx.probe(|_| {
+                        let bound = util::lazily_elaborate_sizedness_candidate(
+                            selcx.infcx,
+                            obligation,
+                            bound,
+                        );
+
                         // We checked the polarity already
                         match selcx.match_normalize_trait_ref(
                             obligation,
@@ -245,14 +265,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             .caller_bounds()
             .iter()
             .filter_map(|p| p.as_trait_clause())
-            // Micro-optimization: filter out predicates relating to different traits.
-            .filter(|p| p.def_id() == stack.obligation.predicate.def_id())
+            // Micro-optimization: filter out predicates with different polarities.
             .filter(|p| p.polarity() == stack.obligation.predicate.polarity());
 
         let drcx = DeepRejectCtxt::relate_rigid_rigid(self.tcx());
         let obligation_args = stack.obligation.predicate.skip_binder().trait_ref.args;
         // Keep only those bounds which may apply, and propagate overflow if it occurs.
         for bound in bounds {
+            let bound =
+                util::lazily_elaborate_sizedness_candidate(self.infcx, stack.obligation, bound);
+
+            // Micro-optimization: filter out predicates relating to different traits.
+            if bound.def_id() != stack.obligation.predicate.def_id() {
+                continue;
+            }
+
             let bound_trait_ref = bound.map_bound(|t| t.trait_ref);
             if !drcx.args_may_unify(obligation_args, bound_trait_ref.skip_binder().args) {
                 continue;
@@ -1086,15 +1113,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
-    /// Assembles the trait which are built-in to the language itself:
-    /// `Copy`, `Clone` and `Sized`.
+    /// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself.
     #[instrument(level = "debug", skip(self, candidates))]
     fn assemble_builtin_sized_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
         candidates: &mut SelectionCandidateSet<'tcx>,
+        sizedness: SizedTraitKind,
     ) {
-        match self.sized_conditions(obligation) {
+        match self.sizedness_conditions(obligation, sizedness) {
             BuiltinImplConditions::Where(nested) => {
                 candidates
                     .vec
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 786afd7cf48..80f71c78993 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -14,7 +14,9 @@ use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
 use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
-use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, Upcast, elaborate};
+use rustc_middle::ty::{
+    self, GenericArgsRef, Region, SizedTraitKind, Ty, TyCtxt, Upcast, elaborate,
+};
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::DefId;
 use thin_vec::thin_vec;
@@ -164,10 +166,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             )
             .break_value()
             .expect("expected to index into clause that exists");
-        let candidate = candidate_predicate
+        let candidate_predicate = candidate_predicate
             .as_trait_clause()
-            .expect("projection candidate is not a trait predicate")
-            .map_bound(|t| t.trait_ref);
+            .expect("projection candidate is not a trait predicate");
+        let candidate_predicate =
+            util::lazily_elaborate_sizedness_candidate(self.infcx, obligation, candidate_predicate);
+
+        let candidate = candidate_predicate.map_bound(|t| t.trait_ref);
 
         let candidate = self.infcx.instantiate_binder_with_fresh_vars(
             obligation.cause.span,
@@ -224,6 +229,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) -> PredicateObligations<'tcx> {
         debug!(?obligation, ?param, "confirm_param_candidate");
 
+        let param = util::lazily_elaborate_sizedness_candidate(
+            self.infcx,
+            obligation,
+            param.upcast(self.infcx.tcx),
+        )
+        .map_bound(|p| p.trait_ref);
+
         // During evaluation, we already checked that this
         // where-clause trait-ref could be unified with the obligation
         // trait-ref. Repeat that unification now without any
@@ -251,7 +263,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let obligations = if has_nested {
             let trait_def = obligation.predicate.def_id();
             let conditions = match tcx.as_lang_item(trait_def) {
-                Some(LangItem::Sized) => self.sized_conditions(obligation),
+                Some(LangItem::Sized) => {
+                    self.sizedness_conditions(obligation, SizedTraitKind::Sized)
+                }
+                Some(LangItem::MetaSized) => {
+                    self.sizedness_conditions(obligation, SizedTraitKind::MetaSized)
+                }
+                Some(LangItem::PointeeSized) => {
+                    bug!("`PointeeSized` is removing during lowering");
+                }
                 Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation),
                 Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation),
                 other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"),
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 1b9b68fa980..9c0ccb26e53 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -27,8 +27,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
 use rustc_middle::ty::{
-    self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable,
-    TypeVisitableExt, TypingMode, Upcast, elaborate,
+    self, DeepRejectCtxt, GenericArgsRef, PolyProjectionPredicate, SizedTraitKind, Ty, TyCtxt,
+    TypeFoldable, TypeVisitableExt, TypingMode, Upcast, elaborate,
 };
 use rustc_span::{Symbol, sym};
 use tracing::{debug, instrument, trace};
@@ -2094,9 +2094,10 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
 }
 
 impl<'tcx> SelectionContext<'_, 'tcx> {
-    fn sized_conditions(
+    fn sizedness_conditions(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
+        sizedness: SizedTraitKind,
     ) -> BuiltinImplConditions<'tcx> {
         use self::BuiltinImplConditions::{Ambiguous, None, Where};
 
@@ -2126,7 +2127,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 Where(ty::Binder::dummy(Vec::new()))
             }
 
-            ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None,
+            ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness {
+                SizedTraitKind::Sized => None,
+                SizedTraitKind::MetaSized => Where(ty::Binder::dummy(Vec::new())),
+            },
+
+            ty::Foreign(..) => None,
 
             ty::Tuple(tys) => Where(
                 obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])),
@@ -2135,11 +2141,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             ty::Pat(ty, _) => Where(obligation.predicate.rebind(vec![*ty])),
 
             ty::Adt(def, args) => {
-                if let Some(sized_crit) = def.sized_constraint(self.tcx()) {
+                if let Some(crit) = def.sizedness_constraint(self.tcx(), sizedness) {
                     // (*) binder moved here
-                    Where(
-                        obligation.predicate.rebind(vec![sized_crit.instantiate(self.tcx(), args)]),
-                    )
+                    Where(obligation.predicate.rebind(vec![crit.instantiate(self.tcx(), args)]))
                 } else {
                     Where(ty::Binder::dummy(Vec::new()))
                 }
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 0723aebd5d2..a05bae53566 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -4,10 +4,13 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_hir::LangItem;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::PolyTraitObligation;
 pub use rustc_infer::traits::util::*;
 use rustc_middle::bug;
+use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
+    self, PolyTraitPredicate, SizedTraitKind, TraitPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
+    TypeFolder, TypeSuperFoldable, TypeVisitableExt,
 };
 pub use rustc_next_trait_solver::placeholder::BoundVarReplacer;
 use rustc_span::Span;
@@ -362,15 +365,19 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
 }
 
 pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>) -> bool {
-    // Proving `Sized` very often on "obviously sized" types like `&T`, accounts for about 60%
-    // percentage of the predicates we have to prove. No need to canonicalize and all that for
-    // such cases.
+    // Proving `Sized`/`MetaSized`, very often on "obviously sized" types like
+    // `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to
+    // canonicalize and all that for such cases.
     if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
         predicate.kind().skip_binder()
     {
-        if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized)
-            && trait_ref.self_ty().is_trivially_sized(tcx)
-        {
+        let sizedness = match tcx.as_lang_item(trait_ref.def_id()) {
+            Some(LangItem::Sized) => SizedTraitKind::Sized,
+            Some(LangItem::MetaSized) => SizedTraitKind::MetaSized,
+            _ => return false,
+        };
+
+        if trait_ref.self_ty().has_trivial_sizedness(tcx, sizedness) {
             debug!("fast path -- trivial sizedness");
             return true;
         }
@@ -378,3 +385,39 @@ pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
 
     false
 }
+
+/// To improve performance, sizedness traits are not elaborated and so special-casing is required
+/// in the trait solver to find a `Sized` candidate for a `MetaSized` obligation. Returns the
+/// predicate to used in the candidate for such a `obligation`, given a `candidate`.
+pub(crate) fn lazily_elaborate_sizedness_candidate<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    obligation: &PolyTraitObligation<'tcx>,
+    candidate: PolyTraitPredicate<'tcx>,
+) -> PolyTraitPredicate<'tcx> {
+    if !infcx.tcx.is_lang_item(obligation.predicate.def_id(), LangItem::MetaSized)
+        || !infcx.tcx.is_lang_item(candidate.def_id(), LangItem::Sized)
+    {
+        return candidate;
+    }
+
+    if obligation.predicate.polarity() != candidate.polarity() {
+        return candidate;
+    }
+
+    let drcx = DeepRejectCtxt::relate_rigid_rigid(infcx.tcx);
+    if !drcx.args_may_unify(
+        obligation.predicate.skip_binder().trait_ref.args,
+        candidate.skip_binder().trait_ref.args,
+    ) {
+        return candidate;
+    }
+
+    candidate.map_bound(|c| TraitPredicate {
+        trait_ref: TraitRef::new_from_args(
+            infcx.tcx,
+            obligation.predicate.def_id(),
+            c.trait_ref.args,
+        ),
+        polarity: c.polarity,
+    })
+}
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 416865e861e..d4e6a23f0eb 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -567,6 +567,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
     ) -> PredicateObligations<'tcx> {
+        // PERF: `Sized`'s predicates include `MetaSized`, but both are compiler implemented marker
+        // traits, so `MetaSized` will always be WF if `Sized` is WF and vice-versa. Determining
+        // the nominal obligations of `Sized` would in-effect just elaborate `MetaSized` and make
+        // the compiler do a bunch of work needlessly.
+        if self.tcx().is_lang_item(def_id, LangItem::Sized) {
+            return Default::default();
+        }
+
         let predicates = self.tcx().predicates_of(def_id);
         let mut origins = vec![def_id; predicates.predicates.len()];
         let mut head = predicates;
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 11becea998c..d996ee2b60a 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -1,23 +1,29 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
-use rustc_hir::LangItem;
 use rustc_hir::def::DefKind;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, fold_regions,
+    self, SizedTraitKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
+    fold_regions,
 };
 use rustc_span::DUMMY_SP;
 use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
 use rustc_trait_selection::traits;
 use tracing::instrument;
 
+/// If `ty` implements the given `sizedness` trait, returns `None`. Otherwise, returns the type
+/// that must implement the given `sizedness` for `ty` to implement it.
 #[instrument(level = "debug", skip(tcx), ret)]
-fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+fn sizedness_constraint_for_ty<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    sizedness: SizedTraitKind,
+    ty: Ty<'tcx>,
+) -> Option<Ty<'tcx>> {
     match ty.kind() {
-        // these are always sized
+        // Always `Sized` or `MetaSized`
         ty::Bool
         | ty::Char
         | ty::Int(..)
@@ -35,31 +41,40 @@ fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'
         | ty::Never
         | ty::Dynamic(_, _, ty::DynStar) => None,
 
-        // these are never sized
-        ty::Str | ty::Slice(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => Some(ty),
-
-        ty::Pat(ty, _) => sized_constraint_for_ty(tcx, *ty),
-
-        ty::Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)),
-
-        // recursive case
-        ty::Adt(adt, args) => adt.sized_constraint(tcx).and_then(|intermediate| {
-            let ty = intermediate.instantiate(tcx, args);
-            sized_constraint_for_ty(tcx, ty)
-        }),
+        ty::Str | ty::Slice(..) | ty::Dynamic(_, _, ty::Dyn) => match sizedness {
+            // Never `Sized`
+            SizedTraitKind::Sized => Some(ty),
+            // Always `MetaSized`
+            SizedTraitKind::MetaSized => None,
+        },
 
-        // these can be sized or unsized.
+        // Maybe `Sized` or `MetaSized`
         ty::Param(..) | ty::Alias(..) | ty::Error(_) => Some(ty),
 
         // We cannot instantiate the binder, so just return the *original* type back,
         // but only if the inner type has a sized constraint. Thus we skip the binder,
         // but don't actually use the result from `sized_constraint_for_ty`.
         ty::UnsafeBinder(inner_ty) => {
-            sized_constraint_for_ty(tcx, inner_ty.skip_binder()).map(|_| ty)
+            sizedness_constraint_for_ty(tcx, sizedness, inner_ty.skip_binder()).map(|_| ty)
+        }
+
+        // Never `MetaSized` or `Sized`
+        ty::Foreign(..) => Some(ty),
+
+        // Recursive cases
+        ty::Pat(ty, _) => sizedness_constraint_for_ty(tcx, sizedness, *ty),
+
+        ty::Tuple(tys) => {
+            tys.last().and_then(|&ty| sizedness_constraint_for_ty(tcx, sizedness, ty))
         }
 
+        ty::Adt(adt, args) => adt.sizedness_constraint(tcx, sizedness).and_then(|intermediate| {
+            let ty = intermediate.instantiate(tcx, args);
+            sizedness_constraint_for_ty(tcx, sizedness, ty)
+        }),
+
         ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => {
-            bug!("unexpected type `{ty:?}` in sized_constraint_for_ty")
+            bug!("unexpected type `{ty:?}` in `sizedness_constraint_for_ty`")
         }
     }
 }
@@ -75,15 +90,22 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
     }
 }
 
-/// Calculates the `Sized` constraint.
+/// Returns the type of the last field of a struct ("the constraint") which must implement the
+/// `sizedness` trait for the whole ADT to be considered to implement that `sizedness` trait.
+/// `def_id` is assumed to be the `AdtDef` of a struct and will panic otherwise.
+///
+/// For `Sized`, there are only a few options for the types in the constraint:
+///     - an meta-sized type (str, slices, trait objects, etc)
+///     - an pointee-sized type (extern types)
+///     - a type parameter or projection whose sizedness can't be known
 ///
-/// In fact, there are only a few options for the types in the constraint:
-///     - an obviously-unsized type
+/// For `MetaSized`, there are only a few options for the types in the constraint:
+///     - an pointee-sized type (extern types)
 ///     - a type parameter or projection whose sizedness can't be known
 #[instrument(level = "debug", skip(tcx), ret)]
-fn adt_sized_constraint<'tcx>(
+fn adt_sizedness_constraint<'tcx>(
     tcx: TyCtxt<'tcx>,
-    def_id: DefId,
+    (def_id, sizedness): (DefId, SizedTraitKind),
 ) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
     if let Some(def_id) = def_id.as_local() {
         if let ty::Representability::Infinite(_) = tcx.representability(def_id) {
@@ -93,21 +115,21 @@ fn adt_sized_constraint<'tcx>(
     let def = tcx.adt_def(def_id);
 
     if !def.is_struct() {
-        bug!("`adt_sized_constraint` called on non-struct type: {def:?}");
+        bug!("`adt_sizedness_constraint` called on non-struct type: {def:?}");
     }
 
     let tail_def = def.non_enum_variant().tail_opt()?;
     let tail_ty = tcx.type_of(tail_def.did).instantiate_identity();
 
-    let constraint_ty = sized_constraint_for_ty(tcx, tail_ty)?;
+    let constraint_ty = sizedness_constraint_for_ty(tcx, sizedness, tail_ty)?;
 
-    // perf hack: if there is a `constraint_ty: Sized` bound, then we know
+    // perf hack: if there is a `constraint_ty: {Meta,}Sized` bound, then we know
     // that the type is sized and do not need to check it on the impl.
-    let sized_trait_def_id = tcx.require_lang_item(LangItem::Sized, DUMMY_SP);
+    let sizedness_trait_def_id = sizedness.require_lang_item(tcx);
     let predicates = tcx.predicates_of(def.did()).predicates;
     if predicates.iter().any(|(p, _)| {
         p.as_trait_clause().is_some_and(|trait_pred| {
-            trait_pred.def_id() == sized_trait_def_id
+            trait_pred.def_id() == sizedness_trait_def_id
                 && trait_pred.self_ty().skip_binder() == constraint_ty
         })
     }) {
@@ -369,7 +391,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId)
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         asyncness,
-        adt_sized_constraint,
+        adt_sizedness_constraint,
         param_env,
         typing_env_normalized_for_post_analysis,
         defaultness,
diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs
index b11bcff1d8b..852949d707b 100644
--- a/compiler/rustc_type_ir/src/elaborate.rs
+++ b/compiler/rustc_type_ir/src/elaborate.rs
@@ -4,6 +4,7 @@ use smallvec::smallvec;
 
 use crate::data_structures::HashSet;
 use crate::inherent::*;
+use crate::lang_items::TraitSolverLangItem;
 use crate::outlives::{Component, push_outlives_components};
 use crate::{self as ty, Interner, Upcast as _};
 
@@ -18,6 +19,7 @@ pub struct Elaborator<I: Interner, O> {
     stack: Vec<O>,
     visited: HashSet<ty::Binder<I, ty::PredicateKind<I>>>,
     mode: Filter,
+    elaborate_sized: ElaborateSized,
 }
 
 enum Filter {
@@ -25,6 +27,12 @@ enum Filter {
     OnlySelf,
 }
 
+#[derive(Eq, PartialEq)]
+enum ElaborateSized {
+    Yes,
+    No,
+}
+
 /// Describes how to elaborate an obligation into a sub-obligation.
 pub trait Elaboratable<I: Interner> {
     fn predicate(&self) -> I::Predicate;
@@ -77,13 +85,19 @@ pub fn elaborate<I: Interner, O: Elaboratable<I>>(
     cx: I,
     obligations: impl IntoIterator<Item = O>,
 ) -> Elaborator<I, O> {
-    let mut elaborator =
-        Elaborator { cx, stack: Vec::new(), visited: HashSet::default(), mode: Filter::All };
+    let mut elaborator = Elaborator {
+        cx,
+        stack: Vec::new(),
+        visited: HashSet::default(),
+        mode: Filter::All,
+        elaborate_sized: ElaborateSized::No,
+    };
     elaborator.extend_deduped(obligations);
     elaborator
 }
 
 impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
+    /// Adds `obligations` to the stack.
     fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) {
         // Only keep those bounds that we haven't already seen.
         // This is necessary to prevent infinite recursion in some
@@ -103,6 +117,13 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
         self
     }
 
+    /// Start elaborating `Sized` - reqd during coherence checking, normally skipped to improve
+    /// compiler performance.
+    pub fn elaborate_sized(mut self) -> Self {
+        self.elaborate_sized = ElaborateSized::Yes;
+        self
+    }
+
     fn elaborate(&mut self, elaboratable: &O) {
         let cx = self.cx;
 
@@ -111,6 +132,19 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
             return;
         };
 
+        // PERF(sized-hierarchy): To avoid iterating over sizedness supertraits in
+        // parameter environments, as an optimisation, sizedness supertraits aren't
+        // elaborated, so check if a `Sized` obligation is being elaborated to a
+        // `MetaSized` obligation and emit it. Candidate assembly and confirmation
+        // are modified to check for the `Sized` subtrait when a `MetaSized` obligation
+        // is present.
+        if self.elaborate_sized == ElaborateSized::No
+            && let Some(did) = clause.as_trait_clause().map(|c| c.def_id())
+            && self.cx.is_lang_item(did, TraitSolverLangItem::Sized)
+        {
+            return;
+        }
+
         let bound_clause = clause.kind();
         match bound_clause.skip_binder() {
             ty::ClauseKind::Trait(data) => {
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 436ab9f80b6..608efc7515c 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -11,7 +11,7 @@ use rustc_ast_ir::Mutability;
 use crate::elaborate::Elaboratable;
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
 use crate::relate::Relate;
-use crate::solve::AdtDestructorKind;
+use crate::solve::{AdtDestructorKind, SizedTraitKind};
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, CollectAndApply, Interner, UpcastFrom};
 
@@ -571,7 +571,11 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
     // FIXME: perhaps use `all_fields` and expose `FieldDef`.
     fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>;
 
-    fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>;
+    fn sizedness_constraint(
+        self,
+        interner: I,
+        sizedness: SizedTraitKind,
+    ) -> Option<ty::EarlyBinder<I, I::Ty>>;
 
     fn is_fundamental(self) -> bool;
 
diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs
index 699dd82fb22..3ee6e07b7a5 100644
--- a/compiler/rustc_type_ir/src/lang_items.rs
+++ b/compiler/rustc_type_ir/src/lang_items.rs
@@ -30,7 +30,9 @@ pub enum TraitSolverLangItem {
     FutureOutput,
     Iterator,
     Metadata,
+    MetaSized,
     Option,
+    PointeeSized,
     PointeeTrait,
     Poll,
     Sized,
diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs
index ba777c7c59a..bbbeaa29f84 100644
--- a/compiler/rustc_type_ir/src/solve/mod.rs
+++ b/compiler/rustc_type_ir/src/solve/mod.rs
@@ -8,6 +8,7 @@ use derive_where::derive_where;
 use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
 
+use crate::lang_items::TraitSolverLangItem;
 use crate::search_graph::PathKind;
 use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast};
 
@@ -366,3 +367,24 @@ pub enum AdtDestructorKind {
     NotConst,
     Const,
 }
+
+/// Which sizedness trait - `Sized`, `MetaSized`? `PointeeSized` is omitted as it is removed during
+/// lowering.
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
+pub enum SizedTraitKind {
+    /// `Sized` trait
+    Sized,
+    /// `MetaSized` trait
+    MetaSized,
+}
+
+impl SizedTraitKind {
+    /// Returns `DefId` of corresponding language item.
+    pub fn require_lang_item<I: Interner>(self, cx: I) -> I::DefId {
+        cx.require_lang_item(match self {
+            SizedTraitKind::Sized => TraitSolverLangItem::Sized,
+            SizedTraitKind::MetaSized => TraitSolverLangItem::MetaSized,
+        })
+    }
+}