diff options
47 files changed, 924 insertions, 435 deletions
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 4990a85466e..5dbbc7297ab 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -52,7 +52,7 @@ use rustc_session::{Limit, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; -use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; pub use rustc_type_ir::lift::Lift; use rustc_type_ir::{ CollectAndApply, Interner, TypeFlags, TypeFoldable, WithCachedTypeInfo, elaborate, search_graph, @@ -93,6 +93,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type DefId = DefId; type LocalDefId = LocalDefId; + type TraitId = DefId; type Span = Span; type GenericArgs = ty::GenericArgsRef<'tcx>; @@ -483,20 +484,32 @@ impl<'tcx> Interner for TyCtxt<'tcx> { !self.codegen_fn_attrs(def_id).target_features.is_empty() } - fn require_lang_item(self, lang_item: TraitSolverLangItem) -> DefId { - self.require_lang_item(trait_lang_item_to_lang_item(lang_item), DUMMY_SP) + fn require_lang_item(self, lang_item: SolverLangItem) -> DefId { + self.require_lang_item(solver_lang_item_to_lang_item(lang_item), DUMMY_SP) } - fn is_lang_item(self, def_id: DefId, lang_item: TraitSolverLangItem) -> bool { - self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item)) + fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> DefId { + self.require_lang_item(solver_trait_lang_item_to_lang_item(lang_item), DUMMY_SP) + } + + fn is_lang_item(self, def_id: DefId, lang_item: SolverLangItem) -> bool { + self.is_lang_item(def_id, solver_lang_item_to_lang_item(lang_item)) + } + + fn is_trait_lang_item(self, def_id: DefId, lang_item: SolverTraitLangItem) -> bool { + self.is_lang_item(def_id, solver_trait_lang_item_to_lang_item(lang_item)) } fn is_default_trait(self, def_id: DefId) -> bool { self.is_default_trait(def_id) } - fn as_lang_item(self, def_id: DefId) -> Option<TraitSolverLangItem> { - lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?) + fn as_lang_item(self, def_id: DefId) -> Option<SolverLangItem> { + lang_item_to_solver_lang_item(self.lang_items().from_def_id(def_id)?) + } + + fn as_trait_lang_item(self, def_id: DefId) -> Option<SolverTraitLangItem> { + lang_item_to_solver_trait_lang_item(self.lang_items().from_def_id(def_id)?) } fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator<Item = DefId> { @@ -727,16 +740,19 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } macro_rules! bidirectional_lang_item_map { - ($($name:ident),+ $(,)?) => { - fn trait_lang_item_to_lang_item(lang_item: TraitSolverLangItem) -> LangItem { + ( + $solver_ty:ident, $to_solver:ident, $from_solver:ident; + $($name:ident),+ $(,)? + ) => { + fn $from_solver(lang_item: $solver_ty) -> LangItem { match lang_item { - $(TraitSolverLangItem::$name => LangItem::$name,)+ + $($solver_ty::$name => LangItem::$name,)+ } } - fn lang_item_to_trait_lang_item(lang_item: LangItem) -> Option<TraitSolverLangItem> { + fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { Some(match lang_item { - $(LangItem::$name => TraitSolverLangItem::$name,)+ + $(LangItem::$name => $solver_ty::$name,)+ _ => return None, }) } @@ -744,40 +760,50 @@ macro_rules! bidirectional_lang_item_map { } bidirectional_lang_item_map! { + SolverLangItem, lang_item_to_solver_lang_item, solver_lang_item_to_lang_item; + +// tidy-alphabetical-start + AsyncFnKindUpvars, + AsyncFnOnceOutput, + CallOnceFuture, + CallRefFuture, + CoroutineReturn, + CoroutineYield, + DynMetadata, + FutureOutput, + Metadata, + Option, + Poll, +// tidy-alphabetical-end +} + +bidirectional_lang_item_map! { + SolverTraitLangItem, lang_item_to_solver_trait_lang_item, solver_trait_lang_item_to_lang_item; + // tidy-alphabetical-start AsyncFn, AsyncFnKindHelper, - AsyncFnKindUpvars, AsyncFnMut, AsyncFnOnce, AsyncFnOnceOutput, AsyncIterator, BikeshedGuaranteedNoDrop, - CallOnceFuture, - CallRefFuture, Clone, Copy, Coroutine, - CoroutineReturn, - CoroutineYield, Destruct, DiscriminantKind, Drop, - DynMetadata, Fn, FnMut, FnOnce, FnPtrTrait, FusedIterator, Future, - FutureOutput, Iterator, MetaSized, - Metadata, - Option, PointeeSized, PointeeTrait, - Poll, Sized, TransmuteTrait, Tuple, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 6a836442c32..cffeb6f9807 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1535,7 +1535,20 @@ impl<'v> RootCollector<'_, 'v> { fn process_nested_body(&mut self, def_id: LocalDefId) { match self.tcx.def_kind(def_id) { DefKind::Closure => { - if self.strategy == MonoItemCollectionStrategy::Eager + // for 'pub async fn foo(..)' also trying to monomorphize foo::{closure} + let is_pub_fn_coroutine = + match *self.tcx.type_of(def_id).instantiate_identity().kind() { + ty::Coroutine(cor_id, _args) => { + let tcx = self.tcx; + let parent_id = tcx.parent(cor_id); + tcx.def_kind(parent_id) == DefKind::Fn + && tcx.asyncness(parent_id).is_async() + && tcx.visibility(parent_id).is_public() + } + ty::Closure(..) | ty::CoroutineClosure(..) => false, + _ => unreachable!(), + }; + if (self.strategy == MonoItemCollectionStrategy::Eager || is_pub_fn_coroutine) && !self .tcx .generics_of(self.tcx.typeck_root_def_id(def_id.to_def_id())) diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index f8215a228f5..79219b34e29 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -295,7 +295,7 @@ where ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) } - fn def_id_is_local(&mut self, def_id: I::DefId) -> bool { + fn def_id_is_local(&mut self, def_id: impl DefId<I>) -> bool { match self.in_crate { InCrate::Local { .. } => def_id.is_local(), InCrate::Remote => false, 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 a4a8317912a..be7e4dd4cda 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -7,7 +7,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::lang_items::SolverTraitLangItem; use rustc_type_ir::search_graph::CandidateHeadUsages; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ @@ -54,7 +54,7 @@ where fn with_replaced_self_ty(self, cx: I, self_ty: I::Ty) -> Self; - fn trait_def_id(self, cx: I) -> I::DefId; + fn trait_def_id(self, cx: I) -> I::TraitId; /// Consider a clause, which consists of a "assumption" and some "requirements", /// to satisfy a goal. If the requirements hold, then attempt to satisfy our @@ -516,80 +516,80 @@ where } else if cx.trait_is_alias(trait_def_id) { G::consider_trait_alias_candidate(self, goal) } else { - match cx.as_lang_item(trait_def_id) { - Some(TraitSolverLangItem::Sized) => { + match cx.as_trait_lang_item(trait_def_id) { + Some(SolverTraitLangItem::Sized) => { G::consider_builtin_sizedness_candidates(self, goal, SizedTraitKind::Sized) } - Some(TraitSolverLangItem::MetaSized) => { + Some(SolverTraitLangItem::MetaSized) => { G::consider_builtin_sizedness_candidates(self, goal, SizedTraitKind::MetaSized) } - Some(TraitSolverLangItem::PointeeSized) => { + Some(SolverTraitLangItem::PointeeSized) => { unreachable!("`PointeeSized` is removed during lowering"); } - Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => { + Some(SolverTraitLangItem::Copy | SolverTraitLangItem::Clone) => { G::consider_builtin_copy_clone_candidate(self, goal) } - Some(TraitSolverLangItem::Fn) => { + Some(SolverTraitLangItem::Fn) => { G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::Fn) } - Some(TraitSolverLangItem::FnMut) => { + Some(SolverTraitLangItem::FnMut) => { G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::FnMut) } - Some(TraitSolverLangItem::FnOnce) => { + Some(SolverTraitLangItem::FnOnce) => { G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::FnOnce) } - Some(TraitSolverLangItem::AsyncFn) => { + Some(SolverTraitLangItem::AsyncFn) => { G::consider_builtin_async_fn_trait_candidates(self, goal, ty::ClosureKind::Fn) } - Some(TraitSolverLangItem::AsyncFnMut) => { + Some(SolverTraitLangItem::AsyncFnMut) => { G::consider_builtin_async_fn_trait_candidates( self, goal, ty::ClosureKind::FnMut, ) } - Some(TraitSolverLangItem::AsyncFnOnce) => { + Some(SolverTraitLangItem::AsyncFnOnce) => { G::consider_builtin_async_fn_trait_candidates( self, goal, ty::ClosureKind::FnOnce, ) } - Some(TraitSolverLangItem::FnPtrTrait) => { + Some(SolverTraitLangItem::FnPtrTrait) => { G::consider_builtin_fn_ptr_trait_candidate(self, goal) } - Some(TraitSolverLangItem::AsyncFnKindHelper) => { + Some(SolverTraitLangItem::AsyncFnKindHelper) => { G::consider_builtin_async_fn_kind_helper_candidate(self, goal) } - Some(TraitSolverLangItem::Tuple) => G::consider_builtin_tuple_candidate(self, goal), - Some(TraitSolverLangItem::PointeeTrait) => { + Some(SolverTraitLangItem::Tuple) => G::consider_builtin_tuple_candidate(self, goal), + Some(SolverTraitLangItem::PointeeTrait) => { G::consider_builtin_pointee_candidate(self, goal) } - Some(TraitSolverLangItem::Future) => { + Some(SolverTraitLangItem::Future) => { G::consider_builtin_future_candidate(self, goal) } - Some(TraitSolverLangItem::Iterator) => { + Some(SolverTraitLangItem::Iterator) => { G::consider_builtin_iterator_candidate(self, goal) } - Some(TraitSolverLangItem::FusedIterator) => { + Some(SolverTraitLangItem::FusedIterator) => { G::consider_builtin_fused_iterator_candidate(self, goal) } - Some(TraitSolverLangItem::AsyncIterator) => { + Some(SolverTraitLangItem::AsyncIterator) => { G::consider_builtin_async_iterator_candidate(self, goal) } - Some(TraitSolverLangItem::Coroutine) => { + Some(SolverTraitLangItem::Coroutine) => { G::consider_builtin_coroutine_candidate(self, goal) } - Some(TraitSolverLangItem::DiscriminantKind) => { + Some(SolverTraitLangItem::DiscriminantKind) => { G::consider_builtin_discriminant_kind_candidate(self, goal) } - Some(TraitSolverLangItem::Destruct) => { + Some(SolverTraitLangItem::Destruct) => { G::consider_builtin_destruct_candidate(self, goal) } - Some(TraitSolverLangItem::TransmuteTrait) => { + Some(SolverTraitLangItem::TransmuteTrait) => { G::consider_builtin_transmute_candidate(self, goal) } - Some(TraitSolverLangItem::BikeshedGuaranteedNoDrop) => { + Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => { G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) } _ => Err(NoSolution), @@ -600,7 +600,7 @@ where // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>` - if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Unsize) { + if cx.is_trait_lang_item(trait_def_id, SolverTraitLangItem::Unsize) { candidates.extend(G::consider_structural_builtin_unsize_candidates(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 d25e74e7335..7c5940828da 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 @@ -4,7 +4,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::lang_items::{SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{ @@ -454,7 +454,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: nested.push( ty::TraitRef::new( cx, - cx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper), + cx.require_trait_lang_item(SolverTraitLangItem::AsyncFnKindHelper), [kind_ty, Ty::from_closure_kind(cx, goal_kind)], ) .upcast(cx), @@ -496,7 +496,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: let args = args.as_closure(); let bound_sig = args.sig(); let sig = bound_sig.skip_binder(); - let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); + let future_trait_def_id = cx.require_trait_lang_item(SolverTraitLangItem::Future); // `Closure`s only implement `AsyncFn*` when their return type // implements `Future`. let mut nested = vec![ @@ -514,7 +514,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: } } else { let async_fn_kind_trait_def_id = - cx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper); + cx.require_trait_lang_item(SolverTraitLangItem::AsyncFnKindHelper); // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait @@ -532,7 +532,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: ); } - let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); + let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput); let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { @@ -581,13 +581,13 @@ fn fn_item_to_async_callable<I: Interner>( bound_sig: ty::Binder<I, ty::FnSig<I>>, ) -> Result<(ty::Binder<I, AsyncCallableRelevantTypes<I>>, Vec<I::Predicate>), NoSolution> { let sig = bound_sig.skip_binder(); - let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); + let future_trait_def_id = cx.require_trait_lang_item(SolverTraitLangItem::Future); // `FnDef` and `FnPtr` only implement `AsyncFn*` when their // return type implements `Future`. let nested = vec![ bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), ]; - let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); + let future_output_def_id = cx.require_lang_item(SolverLangItem::FutureOutput); let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { @@ -633,7 +633,7 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>( args: ty::CoroutineClosureArgs<I>, sig: ty::CoroutineClosureSignature<I>, ) -> I::Ty { - let upvars_projection_def_id = cx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars); + let upvars_projection_def_id = cx.require_lang_item(SolverLangItem::AsyncFnKindUpvars); let tupled_upvars_ty = Ty::new_projection( cx, upvars_projection_def_id, @@ -732,7 +732,7 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>( cx: I, self_ty: I::Ty, ) -> Result<Vec<ty::TraitRef<I>>, NoSolution> { - let destruct_def_id = cx.require_lang_item(TraitSolverLangItem::Destruct); + let destruct_def_id = cx.require_trait_lang_item(SolverTraitLangItem::Destruct); match self_ty.kind() { // `ManuallyDrop` is trivially `[const] Destruct` as we do not run any drop glue on it. @@ -751,7 +751,7 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>( Some(AdtDestructorKind::NotConst) => return Err(NoSolution), // `Drop` impl exists, and it's const. Require `Ty: [const] Drop` to hold. Some(AdtDestructorKind::Const) => { - let drop_def_id = cx.require_lang_item(TraitSolverLangItem::Drop); + let drop_def_id = cx.require_trait_lang_item(SolverTraitLangItem::Drop); let drop_trait_ref = ty::TraitRef::new(cx, drop_def_id, [self_ty]); const_conditions.push(drop_trait_ref); } @@ -869,7 +869,7 @@ where // FIXME(associated_const_equality): Also add associated consts to // the requirements here. - for associated_type_def_id in cx.associated_type_def_ids(trait_ref.def_id) { + for associated_type_def_id in cx.associated_type_def_ids(trait_ref.def_id.into()) { // associated types that require `Self: Sized` do not show up in the built-in // implementation of `Trait for dyn Trait`, and can be dropped here. if cx.generics_require_sized_self(associated_type_def_id) { 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 b7ae9994c62..229345065b1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -3,7 +3,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; -use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::solve::inspect::ProbeKind; use rustc_type_ir::{self as ty, Interner, TypingMode, elaborate}; @@ -33,7 +33,7 @@ where self.with_replaced_self_ty(cx, self_ty) } - fn trait_def_id(self, _: I) -> I::DefId { + fn trait_def_id(self, _: I) -> I::TraitId { self.def_id() } @@ -237,7 +237,7 @@ where // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) let output_is_sized_pred = inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output]) + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) }); let requirements = cx .const_conditions(def_id) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index c2745c878dc..710b59f662a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -130,7 +130,7 @@ where } } - fn compute_dyn_compatible_goal(&mut self, trait_def_id: I::DefId) -> QueryResult<I> { + fn compute_dyn_compatible_goal(&mut self, trait_def_id: I::TraitId) -> QueryResult<I> { if self.cx().trait_is_dyn_compatible(trait_def_id) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { 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 93434dce79f..cfdf2007391 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 @@ -5,7 +5,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::lang_items::{SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _}; use tracing::instrument; @@ -103,7 +103,7 @@ where self.with_replaced_self_ty(cx, self_ty) } - fn trait_def_id(self, cx: I) -> I::DefId { + fn trait_def_id(self, cx: I) -> I::TraitId { self.trait_def_id(cx) } @@ -456,7 +456,7 @@ where // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output]) + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) }); let pred = tupled_inputs_and_output @@ -503,7 +503,11 @@ where // (FIXME: technically we only need to check this if the type is a fn ptr...) let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| { - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output_ty]) + ty::TraitRef::new( + cx, + cx.require_trait_lang_item(SolverTraitLangItem::Sized), + [output_ty], + ) }, ); @@ -515,7 +519,7 @@ where coroutine_return_ty, }| { let (projection_term, term) = if cx - .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallOnceFuture) + .is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) { ( ty::AliasTerm::new( @@ -526,7 +530,7 @@ where output_coroutine_ty.into(), ) } else if cx - .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallRefFuture) + .is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) { ( ty::AliasTerm::new( @@ -540,10 +544,9 @@ where ), output_coroutine_ty.into(), ) - } else if cx.is_lang_item( - goal.predicate.def_id(), - TraitSolverLangItem::AsyncFnOnceOutput, - ) { + } else if cx + .is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) + { ( ty::AliasTerm::new( cx, @@ -637,7 +640,7 @@ where goal: Goal<I, Self>, ) -> Result<Candidate<I>, NoSolution> { let cx = ecx.cx(); - let metadata_def_id = cx.require_lang_item(TraitSolverLangItem::Metadata); + let metadata_def_id = cx.require_lang_item(SolverLangItem::Metadata); assert_eq!(metadata_def_id, goal.predicate.def_id()); let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool @@ -664,7 +667,7 @@ where ty::Str | ty::Slice(_) => Ty::new_usize(cx), ty::Dynamic(_, _, ty::Dyn) => { - let dyn_metadata = cx.require_lang_item(TraitSolverLangItem::DynMetadata); + let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); cx.type_of(dyn_metadata) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) } @@ -678,7 +681,7 @@ where ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let sized_predicate = ty::TraitRef::new( cx, - cx.require_lang_item(TraitSolverLangItem::Sized), + cx.require_trait_lang_item(SolverTraitLangItem::Sized), [I::GenericArg::from(goal.predicate.self_ty())], ); ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate)); @@ -821,10 +824,10 @@ where // coroutine yield ty `Poll<Option<I>>`. let wrapped_expected_ty = Ty::new_adt( cx, - cx.adt_def(cx.require_lang_item(TraitSolverLangItem::Poll)), + cx.adt_def(cx.require_lang_item(SolverLangItem::Poll)), cx.mk_args(&[Ty::new_adt( cx, - cx.adt_def(cx.require_lang_item(TraitSolverLangItem::Option)), + cx.adt_def(cx.require_lang_item(SolverLangItem::Option)), cx.mk_args(&[expected_ty.into()]), ) .into()]), @@ -853,10 +856,9 @@ where let coroutine = args.as_coroutine(); - let term = if cx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineReturn) - { + let term = if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineReturn) { coroutine.return_ty().into() - } else if cx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineYield) { + } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CoroutineYield) { coroutine.yield_ty().into() } else { panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id()) @@ -983,13 +985,13 @@ where target_container_def_id: I::DefId, ) -> Result<I::GenericArgs, NoSolution> { let cx = self.cx(); - Ok(if target_container_def_id == impl_trait_ref.def_id { + Ok(if target_container_def_id == impl_trait_ref.def_id.into() { // Default value from the trait definition. No need to rebase. goal.predicate.alias.args } else if target_container_def_id == impl_def_id { // Same impl, no need to fully translate, just a rebase from // the trait is sufficient. - goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id, impl_args) + goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id.into(), impl_args) } else { let target_args = self.fresh_args_for_item(target_container_def_id); let target_trait_ref = @@ -1004,7 +1006,7 @@ where .iter_instantiated(cx, target_args) .map(|pred| goal.with(cx, pred)), ); - goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id, target_args) + goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id.into(), target_args) }) } } 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 891ecab041a..cdcfebf2909 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -3,7 +3,7 @@ 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::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind}; use rustc_type_ir::{ self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef, @@ -39,7 +39,7 @@ where self.with_replaced_self_ty(cx, self_ty) } - fn trait_def_id(self, _: I) -> I::DefId { + fn trait_def_id(self, _: I) -> I::TraitId { self.def_id() } @@ -131,8 +131,8 @@ where ) -> Result<(), NoSolution> { fn trait_def_id_matches<I: Interner>( cx: I, - clause_def_id: I::DefId, - goal_def_id: I::DefId, + clause_def_id: I::TraitId, + goal_def_id: I::TraitId, polarity: PredicatePolarity, ) -> bool { clause_def_id == goal_def_id @@ -141,8 +141,8 @@ where // // `PointeeSized` bounds are syntactic sugar for a lack of bounds so don't need this. || (polarity == PredicatePolarity::Positive - && cx.is_lang_item(clause_def_id, TraitSolverLangItem::Sized) - && cx.is_lang_item(goal_def_id, TraitSolverLangItem::MetaSized)) + && cx.is_trait_lang_item(clause_def_id, SolverTraitLangItem::Sized) + && cx.is_trait_lang_item(goal_def_id, SolverTraitLangItem::MetaSized)) } if let Some(trait_clause) = assumption.as_trait_clause() @@ -177,8 +177,8 @@ where // are syntactic sugar for a lack of bounds so don't need this. // We don't need to check polarity, `fast_reject_assumption` already rejected non-`Positive` // polarity `Sized` assumptions as matching non-`Positive` `MetaSized` goals. - if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized) - && ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized) + if ecx.cx().is_trait_lang_item(goal.predicate.def_id(), SolverTraitLangItem::MetaSized) + && ecx.cx().is_trait_lang_item(trait_clause.def_id(), SolverTraitLangItem::Sized) { let meta_sized_clause = trait_predicate_with_def_id(ecx.cx(), trait_clause, goal.predicate.def_id()); @@ -263,7 +263,7 @@ where ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let nested_obligations = cx - .predicates_of(goal.predicate.def_id()) + .predicates_of(goal.predicate.def_id().into()) .iter_instantiated(cx, goal.predicate.trait_ref.args) .map(|p| goal.with(cx, p)); // While you could think of trait aliases to have a single builtin impl @@ -372,7 +372,7 @@ where // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output]) + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) }); let pred = tupled_inputs_and_output @@ -414,7 +414,7 @@ where |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| { ty::TraitRef::new( cx, - cx.require_lang_item(TraitSolverLangItem::Sized), + cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output_coroutine_ty], ) }, @@ -757,7 +757,7 @@ where cx, ty::TraitRef::new( cx, - cx.require_lang_item(TraitSolverLangItem::Copy), + cx.require_trait_lang_item(SolverTraitLangItem::Copy), [ty], ), ), @@ -857,7 +857,7 @@ where fn trait_predicate_with_def_id<I: Interner>( cx: I, clause: ty::Binder<I, ty::TraitPredicate<I>>, - did: I::DefId, + did: I::TraitId, ) -> I::Clause { clause .map_bound(|c| TraitPredicate { @@ -956,7 +956,11 @@ where GoalSource::ImplWhereBound, goal.with( cx, - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [a_ty]), + ty::TraitRef::new( + cx, + cx.require_trait_lang_item(SolverTraitLangItem::Sized), + [a_ty], + ), ), ); @@ -981,7 +985,7 @@ where // We may upcast to auto traits that are either explicitly listed in // the object type's bounds, or implied by the principal trait ref's // supertraits. - let a_auto_traits: IndexSet<I::DefId> = a_data + let a_auto_traits: IndexSet<I::TraitId> = a_data .auto_traits() .into_iter() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { @@ -1143,7 +1147,7 @@ where cx, ty::TraitRef::new( cx, - cx.require_lang_item(TraitSolverLangItem::Unsize), + cx.require_trait_lang_item(SolverTraitLangItem::Unsize), [a_tail_ty, b_tail_ty], ), ), @@ -1208,7 +1212,9 @@ where // takes precedence over the structural auto trait candidate being // assembled. ty::Coroutine(def_id, _) - if self.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) => + if self + .cx() + .is_trait_lang_item(goal.predicate.def_id(), SolverTraitLangItem::Unpin) => { match self.cx().coroutine_movability(def_id) { Movability::Static => Some(Err(NoSolution)), diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index dc15cc3eb55..2f7c78d497a 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -4,7 +4,7 @@ use smallvec::smallvec; use crate::data_structures::HashSet; use crate::inherent::*; -use crate::lang_items::TraitSolverLangItem; +use crate::lang_items::SolverTraitLangItem; use crate::outlives::{Component, push_outlives_components}; use crate::{self as ty, Interner, Upcast as _}; @@ -140,7 +140,7 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { // 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) + && self.cx.is_trait_lang_item(did, SolverTraitLangItem::Sized) { return; } @@ -166,7 +166,7 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { // Get predicates implied by the trait, or only super predicates if we only care about self predicates. match self.mode { Filter::All => self.extend_deduped( - cx.explicit_implied_predicates_of(data.def_id()) + cx.explicit_implied_predicates_of(data.def_id().into()) .iter_identity() .enumerate() .map(map_to_child_clause), @@ -181,13 +181,15 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { } // `T: [const] Trait` implies `T: [const] Supertrait`. ty::ClauseKind::HostEffect(data) => self.extend_deduped( - cx.explicit_implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| { - elaboratable.child( - trait_ref - .to_host_effect_clause(cx, data.constness) - .instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)), - ) - }), + cx.explicit_implied_const_bounds(data.def_id().into()).iter_identity().map( + |trait_ref| { + elaboratable.child( + trait_ref + .to_host_effect_clause(cx, data.constness) + .instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)), + ) + }, + ), ), ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { // We know that `T: 'a` for some type `T`. We can @@ -312,8 +314,8 @@ impl<I: Interner, O: Elaboratable<I>> Iterator for Elaborator<I, O> { /// and to make size estimates for vtable layout computation. pub fn supertrait_def_ids<I: Interner>( cx: I, - trait_def_id: I::DefId, -) -> impl Iterator<Item = I::DefId> { + trait_def_id: I::TraitId, +) -> impl Iterator<Item = I::TraitId> { let mut set = HashSet::default(); let mut stack = vec![trait_def_id]; diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index e8840bcfaca..eb5b6b4a1b4 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -38,7 +38,7 @@ pub enum TypeError<I: Interner> { Sorts(ExpectedFound<I::Ty>), ArgumentSorts(ExpectedFound<I::Ty>, usize), - Traits(ExpectedFound<I::DefId>), + Traits(ExpectedFound<I::TraitId>), VariadicMismatch(ExpectedFound<bool>), /// Instantiating a type variable with the given type would have diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index d88c88fc6f3..5a05630e1bd 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -130,7 +130,7 @@ pub fn simplify_type<I: Interner>( ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !cx.trait_is_auto(principal_def_id) => { - Some(SimplifiedType::Trait(principal_def_id)) + Some(SimplifiedType::Trait(principal_def_id.into())) } _ => Some(SimplifiedType::MarkerTraitObject), }, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 569570b5783..64063b1a365 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -649,11 +649,11 @@ pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> { pub trait BoundExistentialPredicates<I: Interner>: Copy + Debug + Hash + Eq + Relate<I> + SliceLike<Item = ty::Binder<I, ty::ExistentialPredicate<I>>> { - fn principal_def_id(self) -> Option<I::DefId>; + fn principal_def_id(self) -> Option<I::TraitId>; fn principal(self) -> Option<ty::Binder<I, ty::ExistentialTraitRef<I>>>; - fn auto_traits(self) -> impl IntoIterator<Item = I::DefId>; + fn auto_traits(self) -> impl IntoIterator<Item = I::TraitId>; fn projection_bounds( self, diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e3231244577..21fe7879b29 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -8,7 +8,7 @@ use rustc_index::bit_set::DenseBitSet; use crate::fold::TypeFoldable; use crate::inherent::*; use crate::ir_print::IrPrint; -use crate::lang_items::TraitSolverLangItem; +use crate::lang_items::{SolverLangItem, SolverTraitLangItem}; use crate::relate::Relate; use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult}; use crate::visit::{Flags, TypeVisitable}; @@ -38,6 +38,13 @@ pub trait Interner: type DefId: DefId<Self>; type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>; + /// A `DefId` of a trait. + /// + /// In rustc this is just a `DefId`, but rust-analyzer uses different types for different items. + /// + /// Note: The `TryFrom<DefId>` always succeeds (in rustc), so don't use it to check if some `DefId` + /// is a trait! + type TraitId: DefId<Self> + Into<Self::DefId> + TryFrom<Self::DefId, Error: std::fmt::Debug>; type Span: Span<Self>; type GenericArgs: GenericArgs<Self>; @@ -271,7 +278,7 @@ pub trait Interner: fn explicit_super_predicates_of( self, - def_id: Self::DefId, + def_id: Self::TraitId, ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>; fn explicit_implied_predicates_of( @@ -302,19 +309,25 @@ pub trait Interner: fn has_target_features(self, def_id: Self::DefId) -> bool; - fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId; + fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId; + + fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> Self::TraitId; + + fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool; + + fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool; - fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool; + fn is_default_trait(self, def_id: Self::TraitId) -> bool; - fn is_default_trait(self, def_id: Self::DefId) -> bool; + fn as_lang_item(self, def_id: Self::DefId) -> Option<SolverLangItem>; - fn as_lang_item(self, def_id: Self::DefId) -> Option<TraitSolverLangItem>; + fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option<SolverTraitLangItem>; fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>; fn for_each_relevant_impl( self, - trait_def_id: Self::DefId, + trait_def_id: Self::TraitId, self_ty: Self::Ty, f: impl FnMut(Self::DefId), ); @@ -329,20 +342,20 @@ pub trait Interner: fn impl_polarity(self, impl_def_id: Self::DefId) -> ty::ImplPolarity; - fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool; + fn trait_is_auto(self, trait_def_id: Self::TraitId) -> bool; - fn trait_is_coinductive(self, trait_def_id: Self::DefId) -> bool; + fn trait_is_coinductive(self, trait_def_id: Self::TraitId) -> bool; - fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool; + fn trait_is_alias(self, trait_def_id: Self::TraitId) -> bool; - fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool; + fn trait_is_dyn_compatible(self, trait_def_id: Self::TraitId) -> bool; - fn trait_is_fundamental(self, def_id: Self::DefId) -> bool; + fn trait_is_fundamental(self, def_id: Self::TraitId) -> bool; - fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::TraitId) -> bool; /// Returns `true` if this is an `unsafe trait`. - fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool; + fn trait_is_unsafe(self, trait_def_id: Self::TraitId) -> bool; fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index f9994448e28..31f8f5be588 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -1,40 +1,46 @@ /// Lang items used by the new trait solver. This can be mapped to whatever internal /// representation of `LangItem`s used in the underlying compiler implementation. -pub enum TraitSolverLangItem { +pub enum SolverLangItem { + // tidy-alphabetical-start + AsyncFnKindUpvars, + AsyncFnOnceOutput, + CallOnceFuture, + CallRefFuture, + CoroutineReturn, + CoroutineYield, + DynMetadata, + FutureOutput, + Metadata, + Option, + Poll, + // tidy-alphabetical-end +} + +pub enum SolverTraitLangItem { // tidy-alphabetical-start AsyncFn, AsyncFnKindHelper, - AsyncFnKindUpvars, AsyncFnMut, AsyncFnOnce, AsyncFnOnceOutput, AsyncIterator, BikeshedGuaranteedNoDrop, - CallOnceFuture, - CallRefFuture, Clone, Copy, Coroutine, - CoroutineReturn, - CoroutineYield, Destruct, DiscriminantKind, Drop, - DynMetadata, Fn, FnMut, FnOnce, FnPtrTrait, FusedIterator, Future, - FutureOutput, Iterator, MetaSized, - Metadata, - Option, PointeeSized, PointeeTrait, - Poll, Sized, TransmuteTrait, Tuple, diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index b53eb099c4b..a3300b88c43 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -58,7 +58,7 @@ where derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) )] pub struct TraitRef<I: Interner> { - pub def_id: I::DefId, + pub def_id: I::TraitId, pub args: I::GenericArgs, /// This field exists to prevent the creation of `TraitRef` without /// calling [`TraitRef::new_from_args`]. @@ -68,32 +68,32 @@ pub struct TraitRef<I: Interner> { impl<I: Interner> Eq for TraitRef<I> {} impl<I: Interner> TraitRef<I> { - pub fn new_from_args(interner: I, trait_def_id: I::DefId, args: I::GenericArgs) -> Self { - interner.debug_assert_args_compatible(trait_def_id, args); + pub fn new_from_args(interner: I, trait_def_id: I::TraitId, args: I::GenericArgs) -> Self { + interner.debug_assert_args_compatible(trait_def_id.into(), args); Self { def_id: trait_def_id, args, _use_trait_ref_new_instead: () } } pub fn new( interner: I, - trait_def_id: I::DefId, + trait_def_id: I::TraitId, args: impl IntoIterator<Item: Into<I::GenericArg>>, ) -> Self { let args = interner.mk_args_from_iter(args.into_iter().map(Into::into)); Self::new_from_args(interner, trait_def_id, args) } - pub fn from_assoc(interner: I, trait_id: I::DefId, args: I::GenericArgs) -> TraitRef<I> { - let generics = interner.generics_of(trait_id); + pub fn from_assoc(interner: I, trait_id: I::TraitId, args: I::GenericArgs) -> TraitRef<I> { + let generics = interner.generics_of(trait_id.into()); TraitRef::new(interner, trait_id, args.iter().take(generics.count())) } /// Returns a `TraitRef` of the form `P0: Foo<P1..Pn>` where `Pi` /// are the parameters defined on trait. - pub fn identity(interner: I, def_id: I::DefId) -> TraitRef<I> { + pub fn identity(interner: I, def_id: I::TraitId) -> TraitRef<I> { TraitRef::new_from_args( interner, def_id, - I::GenericArgs::identity_for_item(interner, def_id), + I::GenericArgs::identity_for_item(interner, def_id.into()), ) } @@ -116,7 +116,7 @@ impl<I: Interner> ty::Binder<I, TraitRef<I>> { self.map_bound_ref(|tr| tr.self_ty()) } - pub fn def_id(&self) -> I::DefId { + pub fn def_id(&self) -> I::TraitId { self.skip_binder().def_id } @@ -155,7 +155,7 @@ impl<I: Interner> TraitPredicate<I> { } } - pub fn def_id(self) -> I::DefId { + pub fn def_id(self) -> I::TraitId { self.trait_ref.def_id } @@ -165,7 +165,7 @@ impl<I: Interner> TraitPredicate<I> { } impl<I: Interner> ty::Binder<I, TraitPredicate<I>> { - pub fn def_id(self) -> I::DefId { + pub fn def_id(self) -> I::TraitId { // Ok to skip binder since trait `DefId` does not care about regions. self.skip_binder().def_id() } @@ -285,7 +285,7 @@ pub enum ExistentialPredicate<I: Interner> { /// E.g., `Iterator::Item = T`. Projection(ExistentialProjection<I>), /// E.g., `Send`. - AutoTrait(I::DefId), + AutoTrait(I::TraitId), } impl<I: Interner> Eq for ExistentialPredicate<I> {} @@ -301,13 +301,14 @@ impl<I: Interner> ty::Binder<I, ExistentialPredicate<I>> { self.rebind(p.with_self_ty(cx, self_ty)).upcast(cx) } ExistentialPredicate::AutoTrait(did) => { - let generics = cx.generics_of(did); + let generics = cx.generics_of(did.into()); let trait_ref = if generics.count() == 1 { ty::TraitRef::new(cx, did, [self_ty]) } else { // If this is an ill-formed auto trait, then synthesize // new error args for the missing generics. - let err_args = GenericArgs::extend_with_error(cx, did, &[self_ty.into()]); + let err_args = + GenericArgs::extend_with_error(cx, did.into(), &[self_ty.into()]); ty::TraitRef::new_from_args(cx, did, err_args) }; self.rebind(trait_ref).upcast(cx) @@ -330,7 +331,7 @@ impl<I: Interner> ty::Binder<I, ExistentialPredicate<I>> { derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) )] pub struct ExistentialTraitRef<I: Interner> { - pub def_id: I::DefId, + pub def_id: I::TraitId, pub args: I::GenericArgs, /// This field exists to prevent the creation of `ExistentialTraitRef` without /// calling [`ExistentialTraitRef::new_from_args`]. @@ -340,14 +341,14 @@ pub struct ExistentialTraitRef<I: Interner> { impl<I: Interner> Eq for ExistentialTraitRef<I> {} impl<I: Interner> ExistentialTraitRef<I> { - pub fn new_from_args(interner: I, trait_def_id: I::DefId, args: I::GenericArgs) -> Self { - interner.debug_assert_existential_args_compatible(trait_def_id, args); + pub fn new_from_args(interner: I, trait_def_id: I::TraitId, args: I::GenericArgs) -> Self { + interner.debug_assert_existential_args_compatible(trait_def_id.into(), args); Self { def_id: trait_def_id, args, _use_existential_trait_ref_new_instead: () } } pub fn new( interner: I, - trait_def_id: I::DefId, + trait_def_id: I::TraitId, args: impl IntoIterator<Item: Into<I::GenericArg>>, ) -> Self { let args = interner.mk_args_from_iter(args.into_iter().map(Into::into)); @@ -378,7 +379,7 @@ impl<I: Interner> ExistentialTraitRef<I> { } impl<I: Interner> ty::Binder<I, ExistentialTraitRef<I>> { - pub fn def_id(&self) -> I::DefId { + pub fn def_id(&self) -> I::TraitId { self.skip_binder().def_id } @@ -439,7 +440,7 @@ impl<I: Interner> ExistentialProjection<I> { let def_id = interner.parent(self.def_id); let args_count = interner.generics_of(def_id).count() - 1; let args = interner.mk_args(&self.args.as_slice()[..args_count]); - ExistentialTraitRef { def_id, args, _use_existential_trait_ref_new_instead: () } + ExistentialTraitRef::new_from_args(interner, def_id.try_into().unwrap(), args) } pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate<I> { @@ -675,7 +676,7 @@ impl<I: Interner> AliasTerm<I> { ) } - pub fn trait_def_id(self, interner: I) -> I::DefId { + pub fn trait_def_id(self, interner: I) -> I::TraitId { assert!( matches!( self.kind(interner), @@ -683,7 +684,7 @@ impl<I: Interner> AliasTerm<I> { ), "expected a projection" ); - interner.parent(self.def_id) + interner.parent(self.def_id).try_into().unwrap() } /// Extracts the underlying trait reference and own args from this projection. @@ -787,7 +788,7 @@ impl<I: Interner> ProjectionPredicate<I> { } } - pub fn trait_def_id(self, interner: I) -> I::DefId { + pub fn trait_def_id(self, interner: I) -> I::TraitId { self.projection_term.trait_def_id(interner) } @@ -799,7 +800,7 @@ impl<I: Interner> ProjectionPredicate<I> { impl<I: Interner> ty::Binder<I, ProjectionPredicate<I>> { /// Returns the `DefId` of the trait of the associated item being projected. #[inline] - pub fn trait_def_id(&self, cx: I) -> I::DefId { + pub fn trait_def_id(&self, cx: I) -> I::TraitId { self.skip_binder().projection_term.trait_def_id(cx) } @@ -847,7 +848,7 @@ impl<I: Interner> NormalizesTo<I> { Self { alias: self.alias.with_replaced_self_ty(interner, self_ty), ..self } } - pub fn trait_def_id(self, interner: I) -> I::DefId { + pub fn trait_def_id(self, interner: I) -> I::TraitId { self.alias.trait_def_id(interner) } @@ -884,13 +885,13 @@ impl<I: Interner> HostEffectPredicate<I> { Self { trait_ref: self.trait_ref.with_replaced_self_ty(interner, self_ty), ..self } } - pub fn def_id(self) -> I::DefId { + pub fn def_id(self) -> I::TraitId { self.trait_ref.def_id } } impl<I: Interner> ty::Binder<I, HostEffectPredicate<I>> { - pub fn def_id(self) -> I::DefId { + pub fn def_id(self) -> I::TraitId { // Ok to skip binder since trait `DefId` does not care about regions. self.skip_binder().def_id() } diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index ff92a0070cc..785d41929cf 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -68,7 +68,7 @@ pub enum PredicateKind<I: Interner> { Clause(ClauseKind<I>), /// Trait must be dyn-compatible. - DynCompatible(I::DefId), + DynCompatible(I::TraitId), /// `T1 <: T2` /// diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 1497236039f..b6d362d77c4 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -7,7 +7,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::lang_items::SolverTraitLangItem; use crate::search_graph::PathKind; use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast}; @@ -386,10 +386,10 @@ pub enum SizedTraitKind { 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, + pub fn require_lang_item<I: Interner>(self, cx: I) -> I::TraitId { + cx.require_trait_lang_item(match self { + SizedTraitKind::Sized => SolverTraitLangItem::Sized, + SizedTraitKind::MetaSized => SolverTraitLangItem::MetaSized, }) } } diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 7d79c384f85..c0fed96d4e6 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -41,6 +41,11 @@ jobs: multiarch: s390x gcc_cross: s390x-linux-gnu qemu: true + - host_target: powerpc64le-unknown-linux-gnu + os: ubuntu-latest + multiarch: ppc64el + gcc_cross: powerpc64le-linux-gnu + qemu: true - host_target: aarch64-apple-darwin os: macos-latest - host_target: i686-pc-windows-msvc diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index b3f5dafab64..ea9c04a3cb5 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -429,9 +429,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb13874a0e55baf4ac3d49d38206aecb31a55b75d6c4d04fd850b53942c8cc8" +checksum = "dd41ead66a69880951b2f7df3139db401d44451b4da123344d27eaa791b89c95" dependencies = [ "anyhow", "rustc_version", diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index 77cb1df8e74..64b56ea114e 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "6" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.21" -rustc-build-sysroot = "0.5.8" +rustc-build-sysroot = "0.5.10" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 59adc572eaa..5e833540002 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f605b57042ffeb320d7ae44490113a827139b766 +269d5b56bcfdf2be82213e72ef9a2e4c592a8c6b diff --git a/src/tools/miri/src/bin/log/mod.rs b/src/tools/miri/src/bin/log/mod.rs index f3b2fdb5348..22f74dd46b5 100644 --- a/src/tools/miri/src/bin/log/mod.rs +++ b/src/tools/miri/src/bin/log/mod.rs @@ -1,2 +1,3 @@ pub mod setup; mod tracing_chrome; +mod tracing_chrome_instant; diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs index 3379816550c..310887a13a5 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome.rs @@ -7,12 +7,15 @@ //! (`git log -- path/to/tracing_chrome.rs`), but in summary: //! - the file attributes were changed and `extern crate` was added at the top //! - if a tracing span has a field called "tracing_separate_thread", it will be given a separate -//! span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing -//! the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior: +//! span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing +//! the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior: //! ```rust //! tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */) //! ``` -//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with Perfetto +//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with +//! Perfetto +//! - use [ChromeLayer::with_elapsed_micros_subtracting_tracing] to make time measurements faster on +//! Linux x86/x86_64 and to subtract time spent tracing from the timestamps in the trace file //! //! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it //! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would @@ -50,9 +53,22 @@ use std::{ thread::JoinHandle, }; +use crate::log::tracing_chrome_instant::TracingChromeInstant; + +/// Contains thread-local data for threads that send tracing spans or events. +struct ThreadData { + /// A unique ID for this thread, will populate "tid" field in the output trace file. + tid: usize, + /// A clone of [ChromeLayer::out] to avoid the expensive operation of accessing a mutex + /// every time. This is used to send [Message]s to the thread that saves trace data to file. + out: Sender<Message>, + /// The instant in time this thread was started. All events happening on this thread will be + /// saved to the trace file with a timestamp (the "ts" field) measured relative to this instant. + start: TracingChromeInstant, +} + thread_local! { - static OUT: RefCell<Option<Sender<Message>>> = const { RefCell::new(None) }; - static TID: RefCell<Option<usize>> = const { RefCell::new(None) }; + static THREAD_DATA: RefCell<Option<ThreadData>> = const { RefCell::new(None) }; } type NameFn<S> = Box<dyn Fn(&EventOrSpan<'_, '_, S>) -> String + Send + Sync>; @@ -64,7 +80,6 @@ where S: Subscriber + for<'span> LookupSpan<'span> + Send + Sync, { out: Arc<Mutex<Sender<Message>>>, - start: std::time::Instant, max_tid: AtomicUsize, include_args: bool, include_locations: bool, @@ -323,7 +338,6 @@ where { fn new(mut builder: ChromeLayerBuilder<S>) -> (ChromeLayer<S>, FlushGuard) { let (tx, rx) = mpsc::channel(); - OUT.with(|val| val.replace(Some(tx.clone()))); let out_writer = builder .out_writer @@ -443,7 +457,6 @@ where }; let layer = ChromeLayer { out: Arc::new(Mutex::new(tx)), - start: std::time::Instant::now(), max_tid: AtomicUsize::new(0), name_fn: builder.name_fn.take(), cat_fn: builder.cat_fn.take(), @@ -456,22 +469,7 @@ where (layer, guard) } - fn get_tid(&self) -> (usize, bool) { - TID.with(|value| { - let tid = *value.borrow(); - match tid { - Some(tid) => (tid, false), - None => { - let tid = self.max_tid.fetch_add(1, Ordering::SeqCst); - value.replace(Some(tid)); - (tid, true) - } - } - }) - } - - fn get_callsite(&self, data: EventOrSpan<S>) -> Callsite { - let (tid, new_thread) = self.get_tid(); + fn get_callsite(&self, data: EventOrSpan<S>, tid: usize) -> Callsite { let name = self.name_fn.as_ref().map(|name_fn| name_fn(&data)); let target = self.cat_fn.as_ref().map(|cat_fn| cat_fn(&data)); let meta = match data { @@ -502,14 +500,6 @@ where (None, None) }; - if new_thread { - let name = match std::thread::current().name() { - Some(name) => name.to_owned(), - None => tid.to_string(), - }; - self.send_message(Message::NewThread(tid, name)); - } - Callsite { tid, name, @@ -548,31 +538,55 @@ where } } - fn enter_span(&self, span: SpanRef<S>, ts: f64) { - let callsite = self.get_callsite(EventOrSpan::Span(&span)); + fn enter_span(&self, span: SpanRef<S>, ts: f64, tid: usize, out: &Sender<Message>) { + let callsite = self.get_callsite(EventOrSpan::Span(&span), tid); let root_id = self.get_root_id(span); - self.send_message(Message::Enter(ts, callsite, root_id)); + let _ignored = out.send(Message::Enter(ts, callsite, root_id)); } - fn exit_span(&self, span: SpanRef<S>, ts: f64) { - let callsite = self.get_callsite(EventOrSpan::Span(&span)); + fn exit_span(&self, span: SpanRef<S>, ts: f64, tid: usize, out: &Sender<Message>) { + let callsite = self.get_callsite(EventOrSpan::Span(&span), tid); let root_id = self.get_root_id(span); - self.send_message(Message::Exit(ts, callsite, root_id)); + let _ignored = out.send(Message::Exit(ts, callsite, root_id)); } - fn get_ts(&self) -> f64 { - self.start.elapsed().as_nanos() as f64 / 1000.0 - } + /// Helper function that measures how much time is spent while executing `f` and accounts for it + /// in subsequent calls, with the aim to reduce biases in the data collected by `tracing_chrome` + /// by subtracting the time spent inside tracing functions from the timeline. This makes it so + /// that the time spent inside the `tracing_chrome` functions does not impact the timestamps + /// inside the trace file (i.e. `ts`), even if such functions are slow (e.g. because they need + /// to format arguments on the same thread those arguments are collected on, otherwise memory + /// safety would be broken). + /// + /// `f` is called with the microseconds elapsed since the current thread was started (**not** + /// since the program start!), with the current thread ID (i.e. `tid`), and with a [Sender] that + /// can be used to send a [Message] to the thread that collects [Message]s and saves them to the + /// trace file. + #[inline(always)] + fn with_elapsed_micros_subtracting_tracing(&self, f: impl Fn(f64, usize, &Sender<Message>)) { + THREAD_DATA.with(|value| { + let mut thread_data = value.borrow_mut(); + let (ThreadData { tid, out, start }, new_thread) = match thread_data.as_mut() { + Some(thread_data) => (thread_data, false), + None => { + let tid = self.max_tid.fetch_add(1, Ordering::SeqCst); + let out = self.out.lock().unwrap().clone(); + let start = TracingChromeInstant::setup_for_thread_and_start(tid); + *thread_data = Some(ThreadData { tid, out, start }); + (thread_data.as_mut().unwrap(), true) + } + }; - fn send_message(&self, message: Message) { - OUT.with(move |val| { - if val.borrow().is_some() { - let _ignored = val.borrow().as_ref().unwrap().send(message); - } else { - let out = self.out.lock().unwrap().clone(); - let _ignored = out.send(message); - val.replace(Some(out)); - } + start.with_elapsed_micros_subtracting_tracing(|ts| { + if new_thread { + let name = match std::thread::current().name() { + Some(name) => name.to_owned(), + None => tid.to_string(), + }; + let _ignored = out.send(Message::NewThread(*tid, name)); + } + f(ts, *tid, out); + }); }); } } @@ -586,52 +600,58 @@ where return; } - let ts = self.get_ts(); - self.enter_span(ctx.span(id).expect("Span not found."), ts); + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out); + }); } fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { if self.include_args { - let span = ctx.span(id).unwrap(); - let mut exts = span.extensions_mut(); + self.with_elapsed_micros_subtracting_tracing(|_, _, _| { + let span = ctx.span(id).unwrap(); + let mut exts = span.extensions_mut(); - let args = exts.get_mut::<ArgsWrapper>(); + let args = exts.get_mut::<ArgsWrapper>(); - if let Some(args) = args { - let args = Arc::make_mut(&mut args.args); - values.record(&mut JsonVisitor { object: args }); - } + if let Some(args) = args { + let args = Arc::make_mut(&mut args.args); + values.record(&mut JsonVisitor { object: args }); + } + }); } } fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { - let ts = self.get_ts(); - let callsite = self.get_callsite(EventOrSpan::Event(event)); - self.send_message(Message::Event(ts, callsite)); + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + let callsite = self.get_callsite(EventOrSpan::Event(event), tid); + let _ignored = out.send(Message::Event(ts, callsite)); + }); } fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { if let TraceStyle::Async = self.trace_style { return; } - let ts = self.get_ts(); - self.exit_span(ctx.span(id).expect("Span not found."), ts); + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + self.exit_span(ctx.span(id).expect("Span not found."), ts, tid, out); + }); } fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { - if self.include_args { - let mut args = Object::new(); - attrs.record(&mut JsonVisitor { object: &mut args }); - ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper { - args: Arc::new(args), - }); - } - if let TraceStyle::Threaded = self.trace_style { - return; - } + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + if self.include_args { + let mut args = Object::new(); + attrs.record(&mut JsonVisitor { object: &mut args }); + ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper { + args: Arc::new(args), + }); + } + if let TraceStyle::Threaded = self.trace_style { + return; + } - let ts = self.get_ts(); - self.enter_span(ctx.span(id).expect("Span not found."), ts); + self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out); + }); } fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { @@ -639,8 +659,9 @@ where return; } - let ts = self.get_ts(); - self.exit_span(ctx.span(&id).expect("Span not found."), ts); + self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| { + self.exit_span(ctx.span(&id).expect("Span not found."), ts, tid, out); + }); } } diff --git a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs new file mode 100644 index 00000000000..f400bc20a7b --- /dev/null +++ b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs @@ -0,0 +1,183 @@ +//! Code in this class was in part inspired by +//! <https://github.com/tikv/minstant/blob/27c9ec5ec90b5b67113a748a4defee0d2519518c/src/tsc_now.rs>. +//! A useful resource is also +//! <https://www.pingcap.com/blog/how-we-trace-a-kv-database-with-less-than-5-percent-performance-impact/>, +//! although this file does not implement TSC synchronization but insteads pins threads to CPUs, +//! since the former is not reliable (i.e. it might lead to non-monotonic time measurements). +//! Another useful resource for future improvements might be measureme's time measurement utils: +//! <https://github.com/rust-lang/measureme/blob/master/measureme/src/counters.rs>. +//! Documentation about how the Linux kernel chooses a clock source can be found here: +//! <https://btorpey.github.io/blog/2014/02/18/clock-sources-in-linux/>. +#![cfg(feature = "tracing")] + +/// This alternative `TracingChromeInstant` implementation was made entirely to suit the needs of +/// [crate::log::tracing_chrome], and shouldn't be used for anything else. It featues two functions: +/// - [TracingChromeInstant::setup_for_thread_and_start], which sets up the current thread to do +/// proper time tracking and returns a point in time to use as "t=0", and +/// - [TracingChromeInstant::with_elapsed_micros_subtracting_tracing], which allows +/// obtaining how much time elapsed since [TracingChromeInstant::setup_for_thread_and_start] was +/// called while accounting for (and subtracting) the time spent inside tracing-related functions. +/// +/// This measures time using [std::time::Instant], except for x86/x86_64 Linux machines, where +/// [std::time::Instant] is too slow (~1.5us) and thus `rdtsc` is used instead (~5ns). +pub enum TracingChromeInstant { + WallTime { + /// The time at which this instant was created, shifted forward to account + /// for time spent in tracing functions as explained in + /// [TracingChromeInstant::with_elapsed_micros_subtracting_tracing]'s comments. + start_instant: std::time::Instant, + }, + #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] + Tsc { + /// The value in the TSC counter when this instant was created, shifted forward to account + /// for time spent in tracing functions as explained in + /// [TracingChromeInstant::with_elapsed_micros_subtracting_tracing]'s comments. + start_tsc: u64, + /// The period of the TSC counter in microseconds. + tsc_to_microseconds: f64, + }, +} + +impl TracingChromeInstant { + /// Can be thought of as the same as [std::time::Instant::now()], but also does some setup to + /// make TSC stable in case TSC is available. This is supposed to be called (at most) once per + /// thread since the thread setup takes a few milliseconds. + /// + /// WARNING: If TSC is available, `incremental_thread_id` is used to pick to which CPU to pin + /// the current thread. Thread IDs should be assigned contiguously starting from 0. Be aware + /// that the current thread will be restricted to one CPU for the rest of the execution! + pub fn setup_for_thread_and_start(incremental_thread_id: usize) -> TracingChromeInstant { + #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] + if *tsc::IS_TSC_AVAILABLE.get_or_init(tsc::is_tsc_available) { + // We need to lock this thread to a specific CPU, because CPUs' TSC timers might be out + // of sync. + tsc::set_cpu_affinity(incremental_thread_id); + + // Can only use tsc_to_microseconds() and rdtsc() after having set the CPU affinity! + // We compute tsc_to_microseconds anew for every new thread just in case some CPU core + // has a different TSC frequency. + let tsc_to_microseconds = tsc::tsc_to_microseconds(); + let start_tsc = tsc::rdtsc(); + return TracingChromeInstant::Tsc { start_tsc, tsc_to_microseconds }; + } + + let _ = incremental_thread_id; // otherwise we get a warning when the TSC branch is disabled + TracingChromeInstant::WallTime { start_instant: std::time::Instant::now() } + } + + /// Calls `f` with the time elapsed in microseconds since this [TracingChromeInstant] was built + /// by [TracingChromeInstant::setup_for_thread_and_start], while subtracting all time previously + /// spent executing other `f`s passed to this function. This behavior allows subtracting time + /// spent in functions that log tracing data (which `f` is supposed to be) from the tracing time + /// measurements. + /// + /// Note: microseconds are used as the time unit since that's what Chrome trace files should + /// contain, see the definition of the "ts" field in + /// <https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview>. + #[inline(always)] + pub fn with_elapsed_micros_subtracting_tracing(&mut self, f: impl Fn(f64)) { + match self { + TracingChromeInstant::WallTime { start_instant } => { + // Obtain the current time (before executing `f`). + let instant_before_f = std::time::Instant::now(); + + // Using the current time (`instant_before_f`) and the `start_instant` stored in + // `self`, calculate the elapsed time (in microseconds) since this instant was + // instantiated, accounting for any time that was previously spent executing `f`. + // The "accounting" part is not computed in this line, but is rather done by + // shifting forward the `start_instant` down below. + let ts = (instant_before_f - *start_instant).as_nanos() as f64 / 1000.0; + + // Run the function (supposedly a function internal to the tracing infrastructure). + f(ts); + + // Measure how much time was spent executing `f` and shift `start_instant` forward + // by that amount. This "removes" that time from the trace. + *start_instant += std::time::Instant::now() - instant_before_f; + } + + #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] + TracingChromeInstant::Tsc { start_tsc, tsc_to_microseconds } => { + // the comments above also apply here, since it's the same logic + let tsc_before_f = tsc::rdtsc(); + let ts = ((tsc_before_f - *start_tsc) as f64) * (*tsc_to_microseconds); + f(ts); + *start_tsc += tsc::rdtsc() - tsc_before_f; + } + } + } +} + +#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))] +mod tsc { + + pub static IS_TSC_AVAILABLE: std::sync::OnceLock<bool> = std::sync::OnceLock::new(); + + /// Reads the timestamp-counter register. Will give monotonic answers only when called from the + /// same thread, because the TSC of different CPUs might be out of sync. + #[inline(always)] + pub(super) fn rdtsc() -> u64 { + #[cfg(target_arch = "x86")] + use core::arch::x86::_rdtsc; + #[cfg(target_arch = "x86_64")] + use core::arch::x86_64::_rdtsc; + + unsafe { _rdtsc() } + } + + /// Estimates the frequency of the TSC counter by waiting 10ms in a busy loop and + /// looking at how much the TSC increased in the meantime. + pub(super) fn tsc_to_microseconds() -> f64 { + const BUSY_WAIT: std::time::Duration = std::time::Duration::from_millis(10); + let tsc_start = rdtsc(); + let instant_start = std::time::Instant::now(); + while instant_start.elapsed() < BUSY_WAIT { + // `thread::sleep()` is not very precise at waking up the program at the right time, + // so use a busy loop instead. + core::hint::spin_loop(); + } + let tsc_end = rdtsc(); + (BUSY_WAIT.as_nanos() as f64) / 1000.0 / ((tsc_end - tsc_start) as f64) + } + + /// Checks whether the TSC counter is available and runs at a constant rate independently + /// of CPU frequency even across different power states of the CPU (i.e. checks for the + /// `invariant_tsc` CPUID flag). + pub(super) fn is_tsc_available() -> bool { + #[cfg(target_arch = "x86")] + use core::arch::x86::__cpuid; + #[cfg(target_arch = "x86_64")] + use core::arch::x86_64::__cpuid; + + // implemented like https://docs.rs/raw-cpuid/latest/src/raw_cpuid/extended.rs.html#965-967 + const LEAF: u32 = 0x80000007; // this is the leaf for "advanced power management info" + let cpuid = unsafe { __cpuid(LEAF) }; + (cpuid.edx & (1 << 8)) != 0 // EDX bit 8 indicates invariant TSC + } + + /// Forces the current thread to run on a single CPU, which ensures the TSC counter is monotonic + /// (since TSCs of different CPUs might be out-of-sync). `incremental_thread_id` is used to pick + /// to which CPU to pin the current thread, and should be an incremental number that starts from + /// 0. + pub(super) fn set_cpu_affinity(incremental_thread_id: usize) { + let cpu_id = match std::thread::available_parallelism() { + Ok(available_parallelism) => incremental_thread_id % available_parallelism, + _ => panic!("Could not determine CPU count to properly set CPU affinity"), + }; + + let mut set = unsafe { std::mem::zeroed::<libc::cpu_set_t>() }; + unsafe { libc::CPU_SET(cpu_id, &mut set) }; + + // Set the current thread's core affinity. + if unsafe { + libc::sched_setaffinity( + 0, // Defaults to current thread + size_of::<libc::cpu_set_t>(), + &set as *const _, + ) + } != 0 + { + panic!("Could not set CPU affinity") + } + } +} diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index ad2a67160f4..bed65440dc9 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -125,81 +125,64 @@ pub struct NewPermission { /// Whether a read access should be performed on the non-frozen /// part on a retag. nonfreeze_access: bool, + /// Permission for memory outside the range. + outside_perm: Permission, /// Whether this pointer is part of the arguments of a function call. /// `protector` is `Some(_)` for all pointers marked `noalias`. protector: Option<ProtectorKind>, } impl<'tcx> NewPermission { - /// Determine NewPermission of the reference from the type of the pointee. - fn from_ref_ty( + /// Determine NewPermission of the reference/Box from the type of the pointee. + /// + /// A `ref_mutability` of `None` indicates a `Box` type. + fn new( pointee: Ty<'tcx>, - mutability: Mutability, - kind: RetagKind, + ref_mutability: Option<Mutability>, + retag_kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>, ) -> Option<Self> { let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env()); - let is_protected = kind == RetagKind::FnEntry; - let protector = is_protected.then_some(ProtectorKind::StrongProtector); - - Some(match mutability { - Mutability::Mut if ty_is_unpin => - NewPermission { - freeze_perm: Permission::new_reserved( - /* ty_is_freeze */ true, - is_protected, - ), - freeze_access: true, - nonfreeze_perm: Permission::new_reserved( - /* ty_is_freeze */ false, - is_protected, - ), - // If we have a mutable reference, then the non-frozen part will - // have state `ReservedIM` or `Reserved`, which can have an initial read access - // performed on it because you cannot have multiple mutable borrows. - nonfreeze_access: true, - protector, - }, - Mutability::Not => - NewPermission { - freeze_perm: Permission::new_frozen(), - freeze_access: true, - nonfreeze_perm: Permission::new_cell(), - // If it is a shared reference, then the non-frozen - // part will have state `Cell`, which should not have an initial access, - // as this can cause data races when using thread-safe data types like - // `Mutex<T>`. - nonfreeze_access: false, - protector, - }, - _ => return None, - }) - } + let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env()); + let is_protected = retag_kind == RetagKind::FnEntry; - /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled). - /// These pointers allow deallocation so need a different kind of protector not handled - /// by `from_ref_ty`. - fn from_unique_ty( - ty: Ty<'tcx>, - kind: RetagKind, - cx: &crate::MiriInterpCx<'tcx>, - ) -> Option<Self> { - let pointee = ty.builtin_deref(true).unwrap(); - pointee.is_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| { - // Regular `Unpin` box, give it `noalias` but only a weak protector - // because it is valid to deallocate it within the function. - let is_protected = kind == RetagKind::FnEntry; - let protector = is_protected.then_some(ProtectorKind::WeakProtector); - NewPermission { - freeze_perm: Permission::new_reserved(/* ty_is_freeze */ true, is_protected), - freeze_access: true, - nonfreeze_perm: Permission::new_reserved( - /* ty_is_freeze */ false, - is_protected, - ), - nonfreeze_access: true, - protector, - } + if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) { + // Mutable reference / Box to pinning type: retagging is a NOP. + // FIXME: with `UnsafePinned`, this should do proper per-byte tracking. + return None; + } + + let freeze_perm = match ref_mutability { + // Shared references are frozen. + Some(Mutability::Not) => Permission::new_frozen(), + // Mutable references and Boxes are reserved. + _ => Permission::new_reserved_frz(), + }; + let nonfreeze_perm = match ref_mutability { + // Shared references are "transparent". + Some(Mutability::Not) => Permission::new_cell(), + // *Protected* mutable references and boxes are reserved without regarding for interior mutability. + _ if is_protected => Permission::new_reserved_frz(), + // Unprotected mutable references and boxes start in `ReservedIm`. + _ => Permission::new_reserved_im(), + }; + + // Everything except for `Cell` gets an initial access. + let initial_access = |perm: &Permission| !perm.is_cell(); + + Some(NewPermission { + freeze_perm, + freeze_access: initial_access(&freeze_perm), + nonfreeze_perm, + nonfreeze_access: initial_access(&nonfreeze_perm), + outside_perm: if ty_is_freeze { freeze_perm } else { nonfreeze_perm }, + protector: is_protected.then_some(if ref_mutability.is_some() { + // Strong protector for references + ProtectorKind::StrongProtector + } else { + // Weak protector for boxes + ProtectorKind::WeakProtector + }), }) } } @@ -313,30 +296,20 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let span = this.machine.current_span(); - // Store initial permissions and their corresponding range. - let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new( - ptr_size, - LocationState::new_accessed(Permission::new_disabled(), IdempotentForeignAccess::None), // this will be overwritten - ); - // Keep track of whether the node has any part that allows for interior mutability. - // FIXME: This misses `PhantomData<UnsafeCell<T>>` which could be considered a marker - // for requesting interior mutability. - let mut has_unsafe_cell = false; - // When adding a new node, the SIFA of its parents needs to be updated, potentially across // the entire memory range. For the parts that are being accessed below, the access itself - // trivially takes care of that. However, we have to do some more work to also deal with - // the parts that are not being accessed. Specifically what we do is that we - // call `update_last_accessed_after_retag` on the SIFA of the permission set for the part of - // memory outside `perm_map` -- so that part is definitely taken care of. The remaining concern - // is the part of memory that is in the range of `perms_map`, but not accessed below. - // There we have two cases: - // * If we do have an `UnsafeCell` (`has_unsafe_cell` becomes true), then the non-accessed part - // uses `nonfreeze_perm`, so the `nonfreeze_perm` initialized parts are also fine. We enforce - // the `freeze_perm` parts to be accessed, and thus everything is taken care of. - // * If there is no `UnsafeCell`, then `freeze_perm` is used everywhere (both inside and outside the initial range), - // and we update everything to have the `freeze_perm`'s SIFA, so there are no issues. (And this assert below is not - // actually needed in this case). + // trivially takes care of that. However, we have to do some more work to also deal with the + // parts that are not being accessed. Specifically what we do is that we call + // `update_last_accessed_after_retag` on the SIFA of the permission set for the part of + // memory outside `perm_map` -- so that part is definitely taken care of. The remaining + // concern is the part of memory that is in the range of `perms_map`, but not accessed + // below. There we have two cases: + // * If the type is `!Freeze`, then the non-accessed part uses `nonfreeze_perm`, so the + // `nonfreeze_perm` initialized parts are also fine. We enforce the `freeze_perm` parts to + // be accessed via the assert below, and thus everything is taken care of. + // * If the type is `Freeze`, then `freeze_perm` is used everywhere (both inside and outside + // the initial range), and we update everything to have the `freeze_perm`'s SIFA, so there + // are no issues. (And this assert below is not actually needed in this case). assert!(new_perm.freeze_access); let protected = new_perm.protector.is_some(); @@ -350,66 +323,48 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .get_tree_borrows_params() .precise_interior_mut; - let default_perm = if !precise_interior_mut { - // NOTE: Using `ty_is_freeze` doesn't give the same result as going through the range - // and computing `has_unsafe_cell`. This is because of zero-sized `UnsafeCell`, for which - // `has_unsafe_cell` is false, but `!ty_is_freeze` is true. - let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env()); - let (perm, access) = if ty_is_freeze { + // Compute initial "inside" permissions. + let loc_state = |frozen: bool| -> LocationState { + let (perm, access) = if frozen { (new_perm.freeze_perm, new_perm.freeze_access) } else { (new_perm.nonfreeze_perm, new_perm.nonfreeze_access) }; let sifa = perm.strongest_idempotent_foreign_access(protected); - let new_loc = if access { + if access { LocationState::new_accessed(perm, sifa) } else { LocationState::new_non_accessed(perm, sifa) - }; - - for (_loc_range, loc) in perms_map.iter_mut_all() { - *loc = new_loc; } - - perm + }; + let perms_map = if !precise_interior_mut { + // For `!Freeze` types, just pretend the entire thing is an `UnsafeCell`. + let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env()); + let state = loc_state(ty_is_freeze); + DedupRangeMap::new(ptr_size, state) } else { + // The initial state will be overwritten by the visitor below. + let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new( + ptr_size, + LocationState::new_accessed( + Permission::new_disabled(), + IdempotentForeignAccess::None, + ), + ); this.visit_freeze_sensitive(place, ptr_size, |range, frozen| { - has_unsafe_cell = has_unsafe_cell || !frozen; - - // We are only ever `Frozen` inside the frozen bits. - let (perm, access) = if frozen { - (new_perm.freeze_perm, new_perm.freeze_access) - } else { - (new_perm.nonfreeze_perm, new_perm.nonfreeze_access) - }; - let sifa = perm.strongest_idempotent_foreign_access(protected); - // NOTE: Currently, `access` is false if and only if `perm` is Cell, so this `if` - // doesn't not change whether any code is UB or not. We could just always use - // `new_accessed` and everything would stay the same. But that seems conceptually - // odd, so we keep the initial "accessed" bit of the `LocationState` in sync with whether - // a read access is performed below. - let new_loc = if access { - LocationState::new_accessed(perm, sifa) - } else { - LocationState::new_non_accessed(perm, sifa) - }; - - // Store initial permissions. + let state = loc_state(frozen); for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) { - *loc = new_loc; + *loc = state; } - interp_ok(()) })?; - - // Allow lazily writing to surrounding data if we found an `UnsafeCell`. - if has_unsafe_cell { new_perm.nonfreeze_perm } else { new_perm.freeze_perm } + perms_map }; let alloc_extra = this.get_alloc_extra(alloc_id)?; let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut(); - for (perm_range, perm) in perms_map.iter_mut_all() { + for (perm_range, perm) in perms_map.iter_all() { if perm.is_accessed() { // Some reborrows incur a read access to the parent. // Adjust range to be relative to allocation start (rather than to `place`). @@ -447,7 +402,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { orig_tag, new_tag, perms_map, - default_perm, + new_perm.outside_perm, protected, span, )?; @@ -514,7 +469,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let new_perm = match val.layout.ty.kind() { &ty::Ref(_, pointee, mutability) => - NewPermission::from_ref_ty(pointee, mutability, kind, this), + NewPermission::new(pointee, Some(mutability), kind, this), _ => None, }; if let Some(new_perm) = new_perm { @@ -571,8 +526,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { // Only boxes for the global allocator get any special treatment. if box_ty.is_box_global(*self.ecx.tcx) { + let pointee = place.layout.ty.builtin_deref(true).unwrap(); let new_perm = - NewPermission::from_unique_ty(place.layout.ty, self.kind, self.ecx); + NewPermission::new(pointee, /* not a ref */ None, self.kind, self.ecx); self.retag_ptr_inplace(place, new_perm)?; } interp_ok(()) @@ -591,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match place.layout.ty.kind() { &ty::Ref(_, pointee, mutability) => { let new_perm = - NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx); + NewPermission::new(pointee, Some(mutability), self.kind, self.ecx); self.retag_ptr_inplace(place, new_perm)?; } ty::RawPtr(_, _) => { @@ -643,14 +599,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // never be ReservedIM, the value of the `ty_is_freeze` // argument doesn't matter // (`ty_is_freeze || true` in `new_reserved` will always be `true`). - freeze_perm: Permission::new_reserved( - /* ty_is_freeze */ true, /* protected */ true, - ), + freeze_perm: Permission::new_reserved_frz(), freeze_access: true, - nonfreeze_perm: Permission::new_reserved( - /* ty_is_freeze */ false, /* protected */ true, - ), + nonfreeze_perm: Permission::new_reserved_frz(), nonfreeze_access: true, + outside_perm: Permission::new_reserved_frz(), protector: Some(ProtectorKind::StrongProtector), }; this.tb_retag_place(place, new_perm) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 38863ca0734..390435e58d1 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -272,28 +272,15 @@ impl Permission { /// Default initial permission of a reborrowed mutable reference that is either /// protected or not interior mutable. - fn new_reserved_frz() -> Self { + pub fn new_reserved_frz() -> Self { Self { inner: ReservedFrz { conflicted: false } } } /// Default initial permission of an unprotected interior mutable reference. - fn new_reserved_im() -> Self { + pub fn new_reserved_im() -> Self { Self { inner: ReservedIM } } - /// Wrapper around `new_reserved_frz` and `new_reserved_im` that decides - /// which to call based on the interior mutability and the retag kind (whether there - /// is a protector is relevant because being protected takes priority over being - /// interior mutable) - pub fn new_reserved(ty_is_freeze: bool, protected: bool) -> Self { - // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`, - // interior mutability and protectors interact poorly. - // To eliminate the case of Protected Reserved IM we override interior mutability - // in the case of a protected reference: protected references are always considered - // "freeze" in their reservation phase. - if ty_is_freeze || protected { Self::new_reserved_frz() } else { Self::new_reserved_im() } - } - /// Default initial permission of a reborrowed shared reference. pub fn new_frozen() -> Self { Self { inner: Frozen } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index bb3fc2d80b3..d9b3696e4f8 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -610,7 +610,7 @@ mod spurious_read { }, y: LocStateProt { state: LocationState::new_non_accessed( - Permission::new_reserved(/* freeze */ true, /* protected */ true), + Permission::new_reserved_frz(), IdempotentForeignAccess::default(), ), prot: true, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 0b2ce900414..0136de55216 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1077,7 +1077,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { .target_features .iter() .filter(|&feature| { - feature.kind != TargetFeatureKind::Implied && !ecx.tcx.sess.target_features.contains(&feature.name) + feature.kind != TargetFeatureKind::Implied + && !ecx.tcx.sess.target_features.contains(&feature.name) }) .fold(String::new(), |mut s, feature| { if !s.is_empty() { diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index 4b783ebdc4e..65ca4215c60 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "socket2" diff --git a/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs new file mode 100644 index 00000000000..7d51050f32b --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs @@ -0,0 +1,9 @@ +//@compile-flags: -Zmiri-tree-borrows + +fn main() { + // Since the "inside" part is `!Freeze`, the permission to mutate is gone. + let pair = ((), 1); + let x = &pair.0; + let ptr = (&raw const *x).cast::<i32>().cast_mut(); + unsafe { ptr.write(0) }; //~ERROR: /write access .* forbidden/ +} diff --git a/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr new file mode 100644 index 00000000000..e9800468c57 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr @@ -0,0 +1,21 @@ +error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs:LL:CC + | +LL | unsafe { ptr.write(0) }; + | ^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the accessed tag <TAG> has state Frozen which forbids this child write access +help: the accessed tag <TAG> was created here, in the initial state Frozen + --> tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs:LL:CC + | +LL | let x = &pair.0; + | ^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs index 6a625e597df..82976326a8d 100644 --- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs +++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs @@ -23,7 +23,8 @@ fn main() { not_unpin_not_protected(); write_does_not_invalidate_all_aliases(); box_into_raw_allows_interior_mutable_alias(); - cell_inside_struct() + cell_inside_struct(); + zst(); } // Make sure that reading from an `&mut` does, like reborrowing to `&`, @@ -287,3 +288,22 @@ fn cell_inside_struct() { // Writing to `field1`, which is reserved, should also be allowed. (*a).field1 = 88; } + +/// ZST reborrows on various kinds of dangling pointers are valid. +fn zst() { + unsafe { + // Integer pointer. + let ptr = ptr::without_provenance_mut::<()>(15); + let _ref = &mut *ptr; + + // Out-of-bounds pointer. + let mut b = Box::new(0u8); + let ptr = (&raw mut *b).wrapping_add(15) as *mut (); + let _ref = &mut *ptr; + + // Deallocated pointer. + let ptr = &raw mut *b as *mut (); + drop(b); + let _ref = &mut *ptr; + } +} diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs index abe08f2cd22..7352784ac7a 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs +++ b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs @@ -14,9 +14,11 @@ fn main() { foo(&arr[0]); let pair = (Cell::new(1), 1); - // TODO: Ideally, this would result in UB since the second element - // in `pair` is Frozen. We would need some way to express a - // "shared reference with permission to access surrounding - // interior mutable data". foo(&pair.0); + + // As long as the "inside" part is `!Freeze`, the permission to mutate the "outside" is preserved. + let pair = (Cell::new(()), 1); + let x = &pair.0; + let ptr = (&raw const *x).cast::<i32>().cast_mut(); + unsafe { ptr.write(0) }; } diff --git a/tests/codegen-llvm/async-fn-debug-awaitee-field.rs b/tests/codegen-llvm/async-fn-debug-awaitee-field.rs index 50860c90662..b9d3d9ee66e 100644 --- a/tests/codegen-llvm/async-fn-debug-awaitee-field.rs +++ b/tests/codegen-llvm/async-fn-debug-awaitee-field.rs @@ -18,11 +18,11 @@ pub async fn async_fn_test() { pub async fn foo() {} +// NONMSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]], +// MSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>", +// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo", // NONMSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[GEN_SCOPE:![0-9]*]], // MSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::async_fn_test::async_fn_env$0>", // NONMSVC: [[GEN_SCOPE:!.*]] = !DINamespace(name: "async_fn_test", // CHECK: [[SUSPEND_STRUCT:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend0", scope: [[GEN]], -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE:![0-9]*]], -// NONMSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]], -// MSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>", -// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo", +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE]], diff --git a/tests/codegen-units/item-collection/async-fn-impl.rs b/tests/codegen-units/item-collection/async-fn-impl.rs new file mode 100644 index 00000000000..540fa0b3228 --- /dev/null +++ b/tests/codegen-units/item-collection/async-fn-impl.rs @@ -0,0 +1,10 @@ +//@ edition: 2024 +// When pub async fn is monomorphized, its implementation coroutine is also monomorphized +//@ compile-flags: --crate-type=lib + +//~ MONO_ITEM fn async_fn @@ +//~ MONO_ITEM fn async_fn::{closure#0} @@ +#[unsafe(no_mangle)] +pub async fn async_fn(x: u64) -> bool { + true +} diff --git a/tests/codegen-units/item-collection/opaque-return-impls.rs b/tests/codegen-units/item-collection/opaque-return-impls.rs new file mode 100644 index 00000000000..7d5f4f5b669 --- /dev/null +++ b/tests/codegen-units/item-collection/opaque-return-impls.rs @@ -0,0 +1,89 @@ +//@ only-x86_64-unknown-linux-gnu +//@ compile-flags: -C panic=abort -Zinline-mir=no -Copt-level=0 -Zcross-crate-inline-threshold=never -Zmir-opt-level=0 -Cno-prepopulate-passes +//@ no-prefer-dynamic +//@ edition:2024 +#![crate_type = "lib"] + +trait TestTrait { + fn test_func(&self); +} + +struct TestStruct {} + +impl TestTrait for TestStruct { + fn test_func(&self) { + println!("TestStruct::test_func"); + } +} + +#[inline(never)] +pub fn foo() -> impl TestTrait { + TestStruct {} +} + +//~ MONO_ITEM fn foo +//~ MONO_ITEM fn <TestStruct as TestTrait>::test_func + +trait TestTrait2 { + fn test_func2(&self); +} + +struct TestStruct2 {} + +impl TestTrait2 for TestStruct2 { + fn test_func2(&self) { + println!("TestStruct2::test_func2"); + } +} + +#[inline(never)] +pub fn foo2() -> Box<dyn TestTrait2> { + Box::new(TestStruct2 {}) +} + +//~ MONO_ITEM fn <TestStruct2 as TestTrait2>::test_func2 +//~ MONO_ITEM fn alloc::alloc::exchange_malloc +//~ MONO_ITEM fn foo2 +//~ MONO_ITEM fn std::alloc::Global::alloc_impl +//~ MONO_ITEM fn std::boxed::Box::<TestStruct2>::new +//~ MONO_ITEM fn std::alloc::Layout::from_size_align_unchecked::precondition_check +//~ MONO_ITEM fn std::ptr::NonNull::<T>::new_unchecked::precondition_check + +struct Counter { + count: usize, +} + +impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } +} + +impl Iterator for Counter { + type Item = usize; + + fn next(&mut self) -> Option<Self::Item> { + self.count += 1; + if self.count < 6 { Some(self.count) } else { None } + } +} + +#[inline(never)] +pub fn foo3() -> Box<dyn Iterator<Item = usize>> { + Box::new(Counter::new()) +} + +//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by +//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0} +//~ MONO_ITEM fn <Counter as std::iter::Iterator>::advance_by +//~ MONO_ITEM fn <Counter as std::iter::Iterator>::next +//~ MONO_ITEM fn <Counter as std::iter::Iterator>::nth +//~ MONO_ITEM fn <Counter as std::iter::Iterator>::size_hint +//~ MONO_ITEM fn <Counter as std::iter::Iterator>::try_fold::<std::num::NonZero<usize>, {closure@<Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}}, std::option::Option<std::num::NonZero<usize>>> +//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::FromResidual<std::option::Option<std::convert::Infallible>>>::from_residual +//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::branch +//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::from_output +//~ MONO_ITEM fn foo3 +//~ MONO_ITEM fn std::boxed::Box::<Counter>::new +//~ MONO_ITEM fn Counter::new +//~ MONO_ITEM fn core::fmt::rt::<impl std::fmt::Arguments<'_>>::new_const::<1> diff --git a/tests/coverage/async.cov-map b/tests/coverage/async.cov-map index c528ad525b5..d75c4b5981a 100644 --- a/tests/coverage/async.cov-map +++ b/tests/coverage/async.cov-map @@ -103,21 +103,21 @@ Number of file 0 mappings: 3 Highest counter ID seen: (none) Function name: async::g -Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 16] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 12] Number of files: 1 - file 0 => $DIR/async.rs Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 22) +- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 18) Highest counter ID seen: c0 Function name: async::g::{closure#0} (unused) -Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 17, 00, 18, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02] +Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 13, 00, 14, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02] Number of files: 1 - file 0 => $DIR/async.rs Number of expressions: 0 Number of file 0 mappings: 12 -- Code(Zero) at (prev + 27, 23) to (start + 0, 24) +- Code(Zero) at (prev + 27, 19) to (start + 0, 20) - Code(Zero) at (prev + 1, 11) to (start + 0, 12) - Code(Zero) at (prev + 1, 9) to (start + 0, 10) - Code(Zero) at (prev + 0, 14) to (start + 0, 23) diff --git a/tests/coverage/async.coverage b/tests/coverage/async.coverage index 9fca1b6997d..9409e6b1deb 100644 --- a/tests/coverage/async.coverage +++ b/tests/coverage/async.coverage @@ -24,8 +24,8 @@ LL| | LL| 0|async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()` LL| | - LL| 1|pub async fn g(x: u8) { - ^0 + LL| 1|async fn g(x: u8) { + ^0 LL| 0| match x { LL| 0| y if e().await == y => (), LL| 0| y if f().await == y => (), diff --git a/tests/coverage/async.rs b/tests/coverage/async.rs index da0a1c0b6f0..777ad7ce7c0 100644 --- a/tests/coverage/async.rs +++ b/tests/coverage/async.rs @@ -24,7 +24,7 @@ async fn f() -> u8 { 1 } async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()` -pub async fn g(x: u8) { +async fn g(x: u8) { match x { y if e().await == y => (), y if f().await == y => (), diff --git a/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs index 3fdd65d6c87..965fd832722 100644 --- a/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs +++ b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs @@ -8,7 +8,7 @@ extern crate rustc_type_ir; use rustc_type_ir::Interner; -fn foo<I: Interner>(cx: I, did: I::DefId) { +fn foo<I: Interner>(cx: I, did: I::TraitId) { let _ = cx.trait_is_unsafe(did); //~^ ERROR do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver } diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.rs b/tests/ui/async-await/future-sizes/async-awaiting-fut.rs index a3f0bdc8514..b5f59069f85 100644 --- a/tests/ui/async-await/future-sizes/async-awaiting-fut.rs +++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.rs @@ -1,7 +1,9 @@ -//@ compile-flags: -Z print-type-sizes --crate-type lib +//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib +//@ needs-deterministic-layouts //@ edition:2021 //@ build-pass //@ ignore-pass +//@ only-x86_64 async fn wait() {} diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout index 642e27b2a57..b30c15bcbe6 100644 --- a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout +++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout @@ -48,6 +48,39 @@ print-type-size variant `Returned`: 1024 bytes print-type-size upvar `.arg`: 1024 bytes print-type-size variant `Panicked`: 1024 bytes print-type-size upvar `.arg`: 1024 bytes +print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes +print-type-size field `.waker`: 8 bytes +print-type-size field `.local_waker`: 8 bytes +print-type-size field `.ext`: 16 bytes +print-type-size field `._marker`: 0 bytes +print-type-size field `._marker2`: 0 bytes +print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes +print-type-size field `.filename`: 16 bytes +print-type-size field `.line`: 4 bytes +print-type-size field `.col`: 4 bytes +print-type-size field `._filename`: 0 bytes +print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes +print-type-size variant `Some`: 16 bytes +print-type-size field `.0`: 16 bytes +print-type-size variant `None`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes +print-type-size field `.0`: 16 bytes +print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 16 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of big_fut()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes +print-type-size field `._vtable_ptr`: 8 bytes +print-type-size field `._phantom`: 0 bytes +print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes print-type-size type: `std::mem::ManuallyDrop<bool>`: 1 bytes, alignment: 1 bytes print-type-size field `.value`: 1 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes @@ -70,3 +103,7 @@ print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes diff --git a/tests/ui/async-await/future-sizes/large-arg.rs b/tests/ui/async-await/future-sizes/large-arg.rs index 5fbae22a771..809f7cf1f93 100644 --- a/tests/ui/async-await/future-sizes/large-arg.rs +++ b/tests/ui/async-await/future-sizes/large-arg.rs @@ -1,7 +1,9 @@ -//@ compile-flags: -Z print-type-sizes --crate-type=lib +//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type=lib +//@ needs-deterministic-layouts //@ edition: 2021 //@ build-pass //@ ignore-pass +//@ only-x86_64 pub async fn test() { let _ = a([0u8; 1024]).await; diff --git a/tests/ui/async-await/future-sizes/large-arg.stdout b/tests/ui/async-await/future-sizes/large-arg.stdout index 67168a3d6ef..e00420d1493 100644 --- a/tests/ui/async-await/future-sizes/large-arg.stdout +++ b/tests/ui/async-await/future-sizes/large-arg.stdout @@ -58,3 +58,45 @@ print-type-size variant `Returned`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Panicked`: 1024 bytes print-type-size upvar `.t`: 1024 bytes +print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes +print-type-size field `.waker`: 8 bytes +print-type-size field `.local_waker`: 8 bytes +print-type-size field `.ext`: 16 bytes +print-type-size field `._marker`: 0 bytes +print-type-size field `._marker2`: 0 bytes +print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes +print-type-size field `.filename`: 16 bytes +print-type-size field `.line`: 4 bytes +print-type-size field `.col`: 4 bytes +print-type-size field `._filename`: 0 bytes +print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes +print-type-size variant `Some`: 16 bytes +print-type-size field `.0`: 16 bytes +print-type-size variant `None`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes +print-type-size field `.0`: 16 bytes +print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 16 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of a<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of b<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of c<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes +print-type-size field `._vtable_ptr`: 8 bytes +print-type-size field `._phantom`: 0 bytes +print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Ready`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Pending`: 0 bytes +print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes diff --git a/tests/ui/print_type_sizes/async.rs b/tests/ui/print_type_sizes/async.rs index 805bccbcf63..951e7cd1012 100644 --- a/tests/ui/print_type_sizes/async.rs +++ b/tests/ui/print_type_sizes/async.rs @@ -1,7 +1,9 @@ -//@ compile-flags: -Z print-type-sizes --crate-type lib +//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib +//@ needs-deterministic-layouts //@ edition:2021 //@ build-pass //@ ignore-pass +//@ only-x86_64 #![allow(dropping_copy_types)] diff --git a/tests/ui/print_type_sizes/async.stdout b/tests/ui/print_type_sizes/async.stdout index 83a6962e4cd..d3d6b6471c6 100644 --- a/tests/ui/print_type_sizes/async.stdout +++ b/tests/ui/print_type_sizes/async.stdout @@ -16,6 +16,35 @@ print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment print-type-size variant `MaybeUninit`: 8192 bytes print-type-size field `.uninit`: 0 bytes print-type-size field `.value`: 8192 bytes +print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes +print-type-size field `.waker`: 8 bytes +print-type-size field `.local_waker`: 8 bytes +print-type-size field `.ext`: 16 bytes +print-type-size field `._marker`: 0 bytes +print-type-size field `._marker2`: 0 bytes +print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes +print-type-size field `.filename`: 16 bytes +print-type-size field `.line`: 4 bytes +print-type-size field `.col`: 4 bytes +print-type-size field `._filename`: 0 bytes +print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes +print-type-size variant `Some`: 16 bytes +print-type-size field `.0`: 16 bytes +print-type-size variant `None`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes +print-type-size field `.0`: 16 bytes +print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 16 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes +print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes +print-type-size field `._vtable_ptr`: 8 bytes +print-type-size field `._phantom`: 0 bytes +print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes +print-type-size field `.pointer`: 8 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes print-type-size field `.value`: 1 bytes print-type-size type: `std::mem::MaybeUninit<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes @@ -32,3 +61,7 @@ print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes +print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes |
