diff options
Diffstat (limited to 'compiler')
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, + }) + } +} |
