diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 7439af7aed3ea..dc2262507cd7c 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1523,8 +1523,7 @@ pub struct LayoutS { /// Encodes information about multi-variant layouts. /// Even with `Multiple` variants, a layout still has its own fields! Those are then - /// shared between all variants. One of them will be the discriminant, - /// but e.g. coroutines can have more. + /// shared between all variants. One of them will be the discriminant. /// /// To access all fields of this layout, both `fields` and the fields of the active variant /// must be taken into account. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 71b54a761a2be..8f3efb87479da 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -816,22 +816,18 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }), }; } - ty::CoroutineClosure(_, args) => { - return match args.as_coroutine_closure().upvar_tys().get(field.index()) { + ty::CoroutineClosure(_def_id, args) => { + let upvar_tys = args.as_coroutine_closure().upvar_tys(); + return match upvar_tys.get(field.index()) { Some(&ty) => Ok(ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_coroutine_closure().upvar_tys().len(), - }), + None => Err(FieldAccessError::OutOfRange { field_count: upvar_tys.len() }), }; } - ty::Coroutine(_, args) => { - // Only prefix fields (upvars and current state) are - // accessible without a variant index. - return match args.as_coroutine().prefix_tys().get(field.index()) { - Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_coroutine().prefix_tys().len(), - }), + ty::Coroutine(_def_id, args) => { + let upvar_tys = args.as_coroutine().upvar_tys(); + return match upvar_tys.get(field.index()) { + Some(&ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { field_count: upvar_tys.len() }), }; } ty::Tuple(tys) => { @@ -1905,11 +1901,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // It doesn't make sense to look at a field beyond the prefix; // these require a variant index, and are not initialized in // aggregate rvalues. - match args.as_coroutine().prefix_tys().get(field_index.as_usize()) { + let upvar_tys = args.as_coroutine().upvar_tys(); + match upvar_tys.get(field_index.as_usize()) { Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_coroutine().prefix_tys().len(), - }), + None => Err(FieldAccessError::OutOfRange { field_count: upvar_tys.len() }), } } AggregateKind::CoroutineClosure(_, args) => { @@ -2534,7 +2529,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.prove_aggregate_predicates(aggregate_kind, location); - if *aggregate_kind == AggregateKind::Tuple { + if matches!(aggregate_kind, AggregateKind::Tuple) { // tuple rvalue field type is always the type of the op. Nothing to check here. return; } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 771e5b219589a..042b43936a6ce 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -810,6 +810,9 @@ fn codegen_stmt<'tcx>( let variant_dest = lval.downcast_variant(fx, variant_index); (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::Coroutine(_, _) => { + (FIRST_VARIANT, lval.downcast_variant(fx, FIRST_VARIANT), None) + } _ => (FIRST_VARIANT, lval, None), }; if active_field_index.is_some() { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index e5fecddec528d..c044cfff6c1e1 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1080,7 +1080,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( closure_or_coroutine_di_node: &'ll DIType, ) -> SmallVec<&'ll DIType> { let (&def_id, up_var_tys) = match closure_or_coroutine_ty.kind() { - ty::Coroutine(def_id, args) => (def_id, args.as_coroutine().prefix_tys()), + ty::Coroutine(def_id, args) => (def_id, args.as_coroutine().upvar_tys()), ty::Closure(def_id, args) => (def_id, args.as_closure().upvar_tys()), ty::CoroutineClosure(def_id, args) => (def_id, args.as_coroutine_closure().upvar_tys()), _ => { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 4edef14422e5f..479bdc190933b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -686,12 +686,12 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( let coroutine_layout = cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.kind_ty()).unwrap(); - let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx); let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len(); let tag_base_type = tag_base_type(cx, coroutine_type_and_layout); + let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); let variant_names_type_di_node = build_variant_names_type_di_node( cx, coroutine_type_di_node, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 657e9ce998f93..864da9a2105db 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -326,7 +326,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( coroutine_type_and_layout: TyAndLayout<'tcx>, coroutine_type_di_node: &'ll DIType, coroutine_layout: &CoroutineLayout<'tcx>, - common_upvar_names: &IndexSlice, + _common_upvar_names: &IndexSlice, ) -> &'ll DIType { let variant_name = CoroutineArgs::variant_name(variant_index); let unique_type_id = UniqueTypeId::for_enum_variant_struct_type( @@ -337,7 +337,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( let variant_layout = coroutine_type_and_layout.for_variant(cx, variant_index); - let coroutine_args = match coroutine_type_and_layout.ty.kind() { + let _coroutine_args = match coroutine_type_and_layout.ty.kind() { ty::Coroutine(_, args) => args.as_coroutine(), _ => unreachable!(), }; @@ -355,7 +355,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( ), |cx, variant_struct_type_di_node| { // Fields that just belong to this variant/state - let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count()) + (0..variant_layout.fields.count()) .map(|field_index| { let coroutine_saved_local = coroutine_layout.variant_fields[variant_index] [FieldIdx::from_usize(field_index)]; @@ -377,28 +377,7 @@ pub fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( type_di_node(cx, field_type), ) }) - .collect(); - - // Fields that are common to all states - let common_fields: SmallVec<_> = coroutine_args - .prefix_tys() - .iter() - .zip(common_upvar_names) - .enumerate() - .map(|(index, (upvar_ty, upvar_name))| { - build_field_di_node( - cx, - variant_struct_type_di_node, - upvar_name.as_str(), - cx.size_and_align_of(upvar_ty), - coroutine_type_and_layout.fields.offset(index), - DIFlags::FlagZero, - type_di_node(cx, upvar_ty), - ) - }) - .collect(); - - state_specific_fields.into_iter().chain(common_fields).collect() + .collect() }, |cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty), ) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index d62f560f11f53..ad11c3b3a3b81 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -126,6 +126,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let variant_dest = dest.project_downcast(bx, variant_index); (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::Coroutine(_, _) => { + (FIRST_VARIANT, dest.project_downcast(bx, FIRST_VARIANT), None) + } _ => (FIRST_VARIANT, dest, None), }; if active_field_index.is_some() { diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index db6c2833b9d52..cf4f911b75581 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -303,6 +303,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variant_dest = self.project_downcast(dest, variant_index)?; (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::Coroutine(_def_id, _args) => { + (FIRST_VARIANT, self.project_downcast(dest, FIRST_VARIANT)?, None) + } _ => (FIRST_VARIANT, dest.clone(), None), }; if active_field_index.is_some() { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index a499e4b980fc3..4dfba94fbed92 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -743,14 +743,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; ty::EarlyBinder::bind(f_ty.ty).instantiate(self.tcx, args) + } else if let Some(&ty) = args.as_coroutine().upvar_tys().get(f.as_usize()) + { + ty } else { - let Some(&f_ty) = args.as_coroutine().prefix_tys().get(f.index()) - else { - fail_out_of_bounds(self, location); - return; - }; - - f_ty + fail_out_of_bounds(self, location); + return; }; check_equal(self, location, f_ty); diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 0f62212743086..b09a8457a11f2 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -51,6 +51,9 @@ pub struct CoroutineLayout<'tcx> { /// await). pub variant_source_info: IndexVec, + /// The starting index of upvars. + pub upvar_start: CoroutineSavedLocal, + /// Which saved locals are storage-live at the same time. Locals that do not /// have conflicts with each other are allowed to overlap in the computed /// layout. @@ -101,6 +104,7 @@ impl Debug for CoroutineLayout<'_> { } fmt.debug_struct("CoroutineLayout") + .field("upvar_start", &self.upvar_start) .field("field_tys", &MapPrinter::new(self.field_tys.iter_enumerated())) .field( "variant_fields", @@ -110,7 +114,12 @@ impl Debug for CoroutineLayout<'_> { .map(|(k, v)| (GenVariantPrinter(k), OneLinePrinter(v))), ), ) + .field("field_names", &MapPrinter::new(self.field_names.iter_enumerated())) .field("storage_conflicts", &self.storage_conflicts) + .field( + "variant_source_info", + &MapPrinter::new(self.variant_source_info.iter_enumerated()), + ) .finish() } } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index b86aa601ce8e3..a807bdc607c1f 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -74,7 +74,11 @@ impl<'tcx> PlaceTy<'tcx> { T: ::std::fmt::Debug + Copy, { if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) { - bug!("cannot use non field projection on downcasted place") + bug!( + "cannot use non field projection on downcasted place from {:?} (variant {:?}), got {elem:?}", + self.ty, + self.variant_index + ) } let answer = match *elem { ProjectionElem::Deref => { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 50e68bfdbe7eb..eb700190af571 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -856,7 +856,7 @@ where if i == tag_field { return TyMaybeWithLayout::TyAndLayout(tag_layout(tag)); } - TyMaybeWithLayout::Ty(args.as_coroutine().prefix_tys()[i]) + bug!("coroutine has no prefix field"); } }, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index dd73f0f4a350d..110efd6496f9f 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -609,7 +609,7 @@ impl<'tcx> CoroutineArgs<'tcx> { witness: witness.expect_ty(), tupled_upvars_ty: tupled_upvars_ty.expect_ty(), }, - _ => bug!("coroutine args missing synthetics"), + _ => bug!("coroutine args missing synthetics, got {:?}", self.args), } } @@ -762,13 +762,6 @@ impl<'tcx> CoroutineArgs<'tcx> { }) }) } - - /// This is the types of the fields of a coroutine which are not stored in a - /// variant. - #[inline] - pub fn prefix_tys(self) -> &'tcx List> { - self.upvar_tys() - } } #[derive(Debug, Copy, Clone, HashStable)] diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 44a7dcc82775b..56a9f33f1c4e3 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -288,7 +288,7 @@ where } None if dump_enabled(tcx, A::NAME, def_id) => { - create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? + create_dump_file(tcx, ".dot", true, A::NAME, &pass_name.unwrap_or("-----"), body)? } _ => return (Ok(()), results), diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 693994b5da76f..28c9afe015d8f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -1,6 +1,6 @@ use rustc_index::bit_set::BitSet; -use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; +use rustc_middle::{mir::visit::Visitor, ty}; use crate::{AnalysisDomain, GenKill, GenKillAnalysis}; @@ -12,11 +12,28 @@ use crate::{AnalysisDomain, GenKill, GenKillAnalysis}; /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for /// immovable coroutines. #[derive(Clone, Copy)] -pub struct MaybeBorrowedLocals; +pub struct MaybeBorrowedLocals { + upvar_start: Option<(Local, usize)>, +} impl MaybeBorrowedLocals { + /// `upvar_start` is to signal that upvars are treated as locals, + /// and locals greater than this value refers to upvars accessed + /// through the tuple `ty::CAPTURE_STRUCT_LOCAL`, aka. _1. + pub fn new(upvar_start: Option<(Local, usize)>) -> Self { + Self { upvar_start } + } + pub(super) fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T> { - TransferFunction { trans } + TransferFunction { trans, upvar_start: self.upvar_start } + } + + pub fn domain_size(&self, body: &Body<'_>) -> usize { + if let Some((start, len)) = self.upvar_start { + start.as_usize() + len + } else { + body.local_decls.len() + } } } @@ -26,7 +43,7 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeBorrowedLocals { fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = unborrowed - BitSet::new_empty(body.local_decls().len()) + BitSet::new_empty(self.domain_size(body)) } fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) { @@ -38,7 +55,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { type Idx = Local; fn domain_size(&self, body: &Body<'tcx>) -> usize { - body.local_decls.len() + self.domain_size(body) } fn statement_effect( @@ -72,6 +89,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`. pub(super) struct TransferFunction<'a, T> { trans: &'a mut T, + upvar_start: Option<(Local, usize)>, } impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> @@ -97,7 +115,20 @@ where Rvalue::AddressOf(_, borrowed_place) | Rvalue::Ref(_, BorrowKind::Mut { .. } | BorrowKind::Shared, borrowed_place) => { if !borrowed_place.is_indirect() { - self.trans.gen(borrowed_place.local); + if borrowed_place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvars)) = self.upvar_start + { + match **borrowed_place.projection { + [ProjectionElem::Field(field, _), ..] + if field.as_usize() < nr_upvars => + { + self.trans.gen(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } else { + self.trans.gen(borrowed_place.local); + } } } @@ -132,7 +163,26 @@ where // // [#61069]: https://github.com/rust-lang/rust/pull/61069 if !dropped_place.is_indirect() { - self.trans.gen(dropped_place.local); + if dropped_place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvars)) = self.upvar_start + { + match **dropped_place.projection { + [] => { + for field in 0..nr_upvars { + self.trans.gen(upvar_start + field) + } + self.trans.gen(dropped_place.local) + } + [ProjectionElem::Field(field, _), ..] + if field.as_usize() < nr_upvars => + { + self.trans.gen(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } else { + self.trans.gen(dropped_place.local); + } } } @@ -169,6 +219,6 @@ pub fn borrowed_locals(body: &Body<'_>) -> BitSet { } let mut borrowed = Borrowed(BitSet::new_empty(body.local_decls.len())); - TransferFunction { trans: &mut borrowed }.visit_body(body); + TransferFunction { trans: &mut borrowed, upvar_start: None }.visit_body(body); borrowed.0 } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 334fa9976f037..f07d888b66a69 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -1,8 +1,9 @@ use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ - self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges, + self, CallReturnPlaces, Local, Location, Place, PlaceElem, StatementKind, TerminatorEdges, }; +use rustc_middle::ty; use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis}; @@ -23,7 +24,15 @@ use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis}; /// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals /// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis -pub struct MaybeLiveLocals; +pub struct MaybeLiveLocals { + pub upvars: Option<(Local, usize)>, +} + +impl MaybeLiveLocals { + fn domain_size(&self, body: &mir::Body<'_>) -> usize { + body.local_decls.len() + self.upvars.map_or(0, |(_, nr_upvar)| nr_upvar) + } +} impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals { type Domain = BitSet; @@ -33,7 +42,7 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals { fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = not live - BitSet::new_empty(body.local_decls.len()) + BitSet::new_empty(self.domain_size(body)) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { @@ -41,11 +50,59 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals { } } +fn maybe_upvar_kill( + trans: &mut impl GenKill, + place: &Place<'_>, + upvars: Option<(Local, usize)>, + location: Location, +) { + trace!(?location, ?place, "liveness kill"); + if place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = upvars + { + match **place.projection { + [] => { + for field in 0..nr_upvar { + trans.kill(upvar_start + field) + } + } + [PlaceElem::Field(field, _), ..] => trans.kill(upvar_start + field.as_usize()), + _ => bug!("unexpected upvar access"), + } + } else { + trans.kill(place.local); + } +} + +fn maybe_upvar_gen( + trans: &mut impl GenKill, + place: &Place<'_>, + upvars: Option<(Local, usize)>, + location: Location, +) { + trace!(?location, ?place, "liveness gen"); + if place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = upvars + { + match **place.projection { + [] => { + for field in 0..nr_upvar { + trans.gen(upvar_start + field) + } + } + [PlaceElem::Field(field, _), ..] => trans.gen(upvar_start + field.as_usize()), + _ => bug!("unexpected upvar access"), + } + } else { + trans.gen(place.local); + } +} + impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { type Idx = Local; fn domain_size(&self, body: &mir::Body<'tcx>) -> usize { - body.local_decls.len() + self.domain_size(body) } fn statement_effect( @@ -54,7 +111,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { statement: &mir::Statement<'tcx>, location: Location, ) { - TransferFunction(trans).visit_statement(statement, location); + TransferFunction { trans, upvars: self.upvars }.visit_statement(statement, location); } fn terminator_effect<'mir>( @@ -63,7 +120,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - TransferFunction(trans).visit_terminator(terminator, location); + TransferFunction { trans, upvars: self.upvars }.visit_terminator(terminator, location); terminator.edges() } @@ -74,22 +131,30 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { return_places: CallReturnPlaces<'_, 'tcx>, ) { if let CallReturnPlaces::Yield(resume_place) = return_places { - YieldResumeEffect(trans).visit_place( + YieldResumeEffect { trans, upvars: self.upvars }.visit_place( &resume_place, PlaceContext::MutatingUse(MutatingUseContext::Yield), Location::START, ) } else { + trace!(?return_places, "(return) liveness kill?"); return_places.for_each(|place| { if let Some(local) = place.as_local() { trans.kill(local); + } else if place.local == ty::CAPTURE_STRUCT_LOCAL + && let [PlaceElem::Field(..)] = &**place.projection + { + maybe_upvar_kill(trans, &place, self.upvars, Location::START); } }); } } } -pub struct TransferFunction<'a, T>(pub &'a mut T); +pub struct TransferFunction<'a, T> { + pub trans: &'a mut T, + pub upvars: Option<(Local, usize)>, +} impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> where @@ -113,34 +178,39 @@ where // above. However, if the place looks like `*_5`, this is still unconditionally a use of // `_5`. } else { - self.0.kill(place.local); + maybe_upvar_kill(self.trans, place, self.upvars, location); } } - Some(DefUse::Use) => self.0.gen(place.local), + Some(DefUse::Use) => { + maybe_upvar_gen(self.trans, place, self.upvars, location); + } None => {} } self.visit_projection(place.as_ref(), context, location); } - fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { - DefUse::apply(self.0, local.into(), context); + fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { + DefUse::apply(self.trans, local.into(), context, self.upvars, location); } } -struct YieldResumeEffect<'a, T>(&'a mut T); +struct YieldResumeEffect<'a, T> { + trans: &'a mut T, + upvars: Option<(Local, usize)>, +} impl<'tcx, T> Visitor<'tcx> for YieldResumeEffect<'_, T> where T: GenKill, { fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { - DefUse::apply(self.0, *place, context); + DefUse::apply(self.trans, *place, context, self.upvars, location); self.visit_projection(place.as_ref(), context, location); } - fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) { - DefUse::apply(self.0, local.into(), context); + fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { + DefUse::apply(self.trans, local.into(), context, self.upvars, location); } } @@ -151,10 +221,16 @@ enum DefUse { } impl DefUse { - fn apply(trans: &mut impl GenKill, place: Place<'_>, context: PlaceContext) { + fn apply( + trans: &mut impl GenKill, + place: Place<'_>, + context: PlaceContext, + upvars: Option<(Local, usize)>, + location: Location, + ) { match DefUse::for_place(place, context) { - Some(DefUse::Def) => trans.kill(place.local), - Some(DefUse::Use) => trans.gen(place.local), + Some(DefUse::Def) => maybe_upvar_kill(trans, &place, upvars, location), + Some(DefUse::Use) => maybe_upvar_gen(trans, &place, upvars, location), None => {} } } @@ -281,7 +357,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { return; } } - TransferFunction(trans).visit_statement(statement, location); + TransferFunction { trans, upvars: None }.visit_statement(statement, location); } fn apply_terminator_effect<'mir>( @@ -290,7 +366,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - TransferFunction(trans).visit_terminator(terminator, location); + TransferFunction { trans, upvars: None }.visit_terminator(terminator, location); terminator.edges() } @@ -301,7 +377,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { return_places: CallReturnPlaces<'_, 'tcx>, ) { if let CallReturnPlaces::Yield(resume_place) = return_places { - YieldResumeEffect(trans).visit_place( + YieldResumeEffect { trans, upvars: None }.visit_place( &resume_place, PlaceContext::MutatingUse(MutatingUseContext::Yield), Location::START, diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 29169c31263bd..d0c81022e8aa4 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -1,6 +1,6 @@ use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::*; +use rustc_middle::{mir::*, ty}; use std::borrow::Cow; @@ -23,13 +23,12 @@ impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> { const NAME: &'static str = "maybe_storage_live"; - fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { + fn bottom_value(&self, _: &Body<'tcx>) -> Self::Domain { // bottom = dead - BitSet::new_empty(body.local_decls.len()) + BitSet::new_empty(self.always_live_locals.domain_size()) } fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) { - assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); for local in self.always_live_locals.iter() { on_entry.insert(local); } @@ -43,8 +42,8 @@ impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> { impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { type Idx = Local; - fn domain_size(&self, body: &Body<'tcx>) -> usize { - body.local_decls.len() + fn domain_size(&self, _body: &Body<'tcx>) -> usize { + self.always_live_locals.domain_size() } fn statement_effect( @@ -96,13 +95,12 @@ impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageDead<'a> { const NAME: &'static str = "maybe_storage_dead"; - fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { + fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain { // bottom = live - BitSet::new_empty(body.local_decls.len()) + BitSet::new_empty(self.always_live_locals.domain_size()) } fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) { - assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); // Do not iterate on return place and args, as they are trivially always live. for local in body.vars_and_temps_iter() { if !self.always_live_locals.contains(local) { @@ -115,8 +113,8 @@ impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageDead<'a> { impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageDead<'a> { type Idx = Local; - fn domain_size(&self, body: &Body<'tcx>) -> usize { - body.local_decls.len() + fn domain_size(&self, _body: &Body<'tcx>) -> usize { + self.always_live_locals.domain_size() } fn statement_effect( @@ -158,11 +156,18 @@ type BorrowedLocalsResults<'mir, 'tcx> = ResultsCursor<'mir, 'tcx, MaybeBorrowed /// given location; i.e. whether its storage can go away without being observed. pub struct MaybeRequiresStorage<'mir, 'tcx> { borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>, + upvars: Option<(Local, usize)>, } impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { pub fn new(borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>) -> Self { - MaybeRequiresStorage { borrowed_locals } + MaybeRequiresStorage { borrowed_locals, upvars: None } + } + pub fn new_with_upvar( + borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>, + upvars: (Local, usize), + ) -> Self { + MaybeRequiresStorage { borrowed_locals, upvars: Some(upvars) } } } @@ -173,7 +178,7 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, 'tcx> { fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = dead - BitSet::new_empty(body.local_decls.len()) + BitSet::new_empty(body.local_decls.len() + self.upvars.map_or(0, |(_, nr_upvar)| nr_upvar)) } fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) { @@ -182,6 +187,13 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, 'tcx> { for arg in body.args_iter().skip(1) { on_entry.insert(arg); } + if let Some((upvar_start, nr_upvar)) = self.upvars { + assert_eq!(body.local_decls.next_index(), upvar_start); + // The upvars requires storage on entry, too. + for idx in 0..nr_upvar { + on_entry.insert(upvar_start + idx); + } + } } } @@ -189,7 +201,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { type Idx = Local; fn domain_size(&self, body: &Body<'tcx>) -> usize { - body.local_decls.len() + body.local_decls.len() + self.upvars.map_or(0, |(_, nr_upvar)| nr_upvar) } fn before_statement_effect( @@ -208,7 +220,18 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { StatementKind::Assign(box (place, _)) | StatementKind::SetDiscriminant { box place, .. } | StatementKind::Deinit(box place) => { - trans.gen(place.local); + if place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = self.upvars + { + match **place.projection { + [ProjectionElem::Field(field, _), ..] if field.as_usize() < nr_upvar => { + trans.gen(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } else { + trans.gen(place.local) + } } // Nothing to do for these. Match exhaustively so this fails to compile when new @@ -250,7 +273,18 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { match &terminator.kind { TerminatorKind::Call { destination, .. } => { - trans.gen(destination.local); + if destination.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = self.upvars + { + match **destination.projection { + [ProjectionElem::Field(field, _), ..] if field.as_usize() < nr_upvar => { + trans.gen(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } else { + trans.gen(destination.local); + } } // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for @@ -265,7 +299,20 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { InlineAsmOperand::Out { place, .. } | InlineAsmOperand::InOut { out_place: place, .. } => { if let Some(place) = place { - trans.gen(place.local); + if place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = self.upvars + { + match **place.projection { + [ProjectionElem::Field(field, _), ..] + if field.as_usize() < nr_upvar => + { + trans.gen(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } else { + trans.gen(place.local) + } } } InlineAsmOperand::In { .. } @@ -305,12 +352,56 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { // Since `propagate_call_unwind` doesn't exist, we have to kill the // destination here, and then gen it again in `call_return_effect`. TerminatorKind::Call { destination, .. } => { - trans.kill(destination.local); + if destination.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = self.upvars + { + match **destination.projection { + [ProjectionElem::Field(field, _), ..] if field.as_usize() < nr_upvar => { + trans.kill(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } else { + trans.kill(destination.local); + } } // The same applies to InlineAsm outputs. TerminatorKind::InlineAsm { ref operands, .. } => { - CallReturnPlaces::InlineAsm(operands).for_each(|place| trans.kill(place.local)); + CallReturnPlaces::InlineAsm(operands).for_each(|place| { + if place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = self.upvars + { + match **place.projection { + [ProjectionElem::Field(field, _), ..] + if field.as_usize() < nr_upvar => + { + trans.kill(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } else { + trans.kill(place.local) + } + }); + } + + TerminatorKind::Drop { place, .. } => { + if place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = self.upvars + { + match **place.projection { + [] => { + for field in 0..nr_upvar { + trans.kill(upvar_start + field) + } + } + [ProjectionElem::Field(field, _), ..] if field.as_usize() < nr_upvar => { + trans.kill(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } } // Nothing to do for these. Match exhaustively so this fails to compile when new @@ -318,7 +409,6 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { TerminatorKind::Yield { .. } | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Assert { .. } - | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::CoroutineDrop @@ -339,7 +429,20 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { _block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, ) { - return_places.for_each(|place| trans.gen(place.local)); + return_places.for_each(|place| { + if place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((upvar_start, nr_upvar)) = self.upvars + { + match **place.projection { + [ProjectionElem::Field(field, _), ..] if field.as_usize() < nr_upvar => { + trans.gen(upvar_start + field.as_usize()) + } + _ => bug!("unexpected upvar access"), + } + } else { + trans.gen(place.local) + } + }); } } @@ -347,7 +450,11 @@ impl<'tcx> MaybeRequiresStorage<'_, 'tcx> { /// Kill locals that are fully moved and have not been borrowed. fn check_for_move(&mut self, trans: &mut impl GenKill, loc: Location) { let body = self.borrowed_locals.body(); - let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals }; + let mut visitor = MoveVisitor { + trans, + borrowed_locals: &mut self.borrowed_locals, + upvar_start: self.upvars.map(|(upvar_start, _)| upvar_start), + }; visitor.visit_location(body, loc); } } @@ -355,6 +462,7 @@ impl<'tcx> MaybeRequiresStorage<'_, 'tcx> { struct MoveVisitor<'a, 'mir, 'tcx, T> { borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>, trans: &'a mut T, + upvar_start: Option, } impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx, T> @@ -362,11 +470,28 @@ where T: GenKill, { fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) { - if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context { + if matches!(context, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)) { self.borrowed_locals.seek_before_primary_effect(loc); if !self.borrowed_locals.contains(local) { self.trans.kill(local); } } } + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + if let ( + Operand::Move(Place { local: ty::CAPTURE_STRUCT_LOCAL, projection }), + Some(upvar_start), + ) = (operand, self.upvar_start) + && let [ProjectionElem::Field(field, _)] = ***projection + { + // QUESTION: what to do with subprojection? + self.borrowed_locals.seek_before_primary_effect(location); + let local = upvar_start + field.as_usize(); + if !self.borrowed_locals.contains(local) { + self.trans.kill(local); + } + } else { + self.super_operand(operand, location); + } + } } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index d7e3c91b875f8..02da3356083f5 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -74,7 +74,8 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { - let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); + let flow_liveness = + MaybeLiveLocals { upvars: None }.into_engine(tcx, body).iterate_to_fixpoint(); sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body)); } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index e2a911f0dc7d9..d661b039b497f 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -52,6 +52,7 @@ mod by_move_body; pub use by_move_body::ByMoveBody; +use smallvec::SmallVec; use crate::abort_unwinding_calls; use crate::deref_separator::deref_finder; @@ -64,6 +65,7 @@ use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_hir::{CoroutineDesugaring, CoroutineKind}; use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet}; +use rustc_index::IndexSlice; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -80,7 +82,7 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_target::spec::PanicStrategy; -use std::{iter, ops}; +use std::ops; pub struct StateTransform; @@ -249,9 +251,13 @@ struct TransformVisitor<'tcx> { old_yield_ty: Ty<'tcx>, old_ret_ty: Ty<'tcx>, + + // A marker where the "virtual" upvar local indexing starts + upvar_start: Local, } impl<'tcx> TransformVisitor<'tcx> { + /// Construct a returning block so that the Future/AsyncGen is fused. fn insert_none_ret_block(&self, body: &mut Body<'tcx>) -> BasicBlock { let block = BasicBlock::new(body.basic_blocks.len()); let source_info = SourceInfo::outermost(body.span); @@ -483,15 +489,22 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { assert_eq!(self.remap.get(local), None); } - fn visit_place( - &mut self, - place: &mut Place<'tcx>, - _context: PlaceContext, - _location: Location, - ) { + fn visit_place(&mut self, place: &mut Place<'tcx>, _context: PlaceContext, location: Location) { // Replace an Local in the remap with a coroutine struct access if let Some(&(ty, variant_index, idx)) = self.remap.get(&place.local) { replace_base(place, self.make_field(variant_index, idx, ty), self.tcx); + } else if place.local == ty::CAPTURE_STRUCT_LOCAL { + let [PlaceElem::Field(field, _), more_projections @ ..] = &**place.projection else { + // dropping coroutine struct will be handled later + return; + }; + let Some(&(ty, variant_index, idx)) = + self.remap.get(&(self.upvar_start + field.as_usize())) + else { + bug!("unrecognized upvar {field:?} at {location:?}") + }; + *place = + self.make_field(variant_index, idx, ty).project_deeper(more_projections, self.tcx); } } @@ -568,19 +581,19 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { } fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let coroutine_ty = body.local_decls.raw[1].ty; + let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; let ref_coroutine_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty); // Replace the by value coroutine argument - body.local_decls.raw[1].ty = ref_coroutine_ty; + body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty = ref_coroutine_ty; // Add a deref to accesses of the coroutine state DerefArgVisitor { tcx }.visit_body(body); } fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let ref_coroutine_ty = body.local_decls.raw[1].ty; + let ref_coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; let pin_did = tcx.require_lang_item(LangItem::Pin, Some(body.span)); let pin_adt_ref = tcx.adt_def(pin_did); @@ -588,7 +601,7 @@ fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body let pin_ref_coroutine_ty = Ty::new_adt(tcx, pin_adt_ref, args); // Replace the by ref coroutine argument - body.local_decls.raw[1].ty = pin_ref_coroutine_ty; + body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty = pin_ref_coroutine_ty; // Add the Pin field access to accesses of the coroutine state PinArgVisitor { ref_coroutine_ty, tcx }.visit_body(body); @@ -757,36 +770,49 @@ struct LivenessInfo { /// case none exist, the local is considered to be always live. /// - a local has to be stored if it is either directly used after the /// the suspend point, or if it is live and has been previously borrowed. +#[instrument(level = "debug", skip(tcx, body))] fn locals_live_across_suspend_points<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, always_live_locals: &BitSet, movable: bool, + upvars: &IndexSlice>, ) -> LivenessInfo { + let nr_body_local = body.local_decls().next_index(); + let nr_local_and_upvar = nr_body_local.as_usize() + upvars.len(); + debug!(nr_body_local = body.local_decls().len(), "total body local decls"); + debug!(?nr_local_and_upvar, "total local decls plus upvars"); + let mut always_live_locals = GrowableBitSet::from(always_live_locals.clone()); + always_live_locals.ensure(nr_local_and_upvar); + let always_live_locals: BitSet<_> = always_live_locals.into(); // Calculate when MIR locals have live storage. This gives us an upper bound of their // lifetimes. - let mut storage_live = MaybeStorageLive::new(std::borrow::Cow::Borrowed(always_live_locals)) + let mut storage_live = MaybeStorageLive::new(std::borrow::Cow::Borrowed(&always_live_locals)) .into_engine(tcx, body) .iterate_to_fixpoint() .into_results_cursor(body); // Calculate the MIR locals which have been previously // borrowed (even if they are still active). - let borrowed_locals_results = - MaybeBorrowedLocals.into_engine(tcx, body).pass_name("coroutine").iterate_to_fixpoint(); + let borrowed_locals_results = MaybeBorrowedLocals::new(Some((nr_body_local, upvars.len()))) + .into_engine(tcx, body) + .pass_name("coroutine") + .iterate_to_fixpoint(); let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body); // Calculate the MIR locals that we actually need to keep storage around // for. - let mut requires_storage_cursor = - MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body)) - .into_engine(tcx, body) - .iterate_to_fixpoint() - .into_results_cursor(body); + let mut requires_storage_cursor = MaybeRequiresStorage::new_with_upvar( + borrowed_locals_results.into_results_cursor(body), + (nr_body_local, upvars.len()), + ) + .into_engine(tcx, body) + .iterate_to_fixpoint() + .into_results_cursor(body); // Calculate the liveness of MIR locals ignoring borrows. - let mut liveness = MaybeLiveLocals + let mut liveness = MaybeLiveLocals { upvars: Some((nr_body_local, upvars.len())) } .into_engine(tcx, body) .pass_name("coroutine") .iterate_to_fixpoint() @@ -795,15 +821,21 @@ fn locals_live_across_suspend_points<'tcx>( let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks); let mut live_locals_at_suspension_points = Vec::new(); let mut source_info_at_suspension_points = Vec::new(); - let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len()); + let mut live_locals_at_any_suspension_point = BitSet::new_empty(nr_local_and_upvar); + + // At UNRESUMED, upvars are live + for idx in 0..upvars.len() { + live_locals_at_any_suspension_point.insert(nr_body_local + idx); + } for (block, data) in body.basic_blocks.iter_enumerated() { if let TerminatorKind::Yield { .. } = data.terminator().kind { let loc = Location { block, statement_index: data.statements.len() }; liveness.seek_to_block_end(block); - let mut live_locals: BitSet<_> = BitSet::new_empty(body.local_decls.len()); + let mut live_locals: BitSet<_> = BitSet::new_empty(nr_local_and_upvar); live_locals.union(liveness.get()); + debug!(?live_locals, "+= liveness"); if !movable { // The `liveness` variable contains the liveness of MIR locals ignoring borrows. @@ -818,6 +850,7 @@ fn locals_live_across_suspend_points<'tcx>( // of the local, which happens using the `intersect` operation below. borrowed_locals_cursor.seek_before_primary_effect(loc); live_locals.union(borrowed_locals_cursor.get()); + debug!(?live_locals, "+= borrowed"); } // Store the storage liveness for later use so we can restore the state @@ -858,7 +891,7 @@ fn locals_live_across_suspend_points<'tcx>( let storage_conflicts = compute_storage_conflicts( body, &saved_locals, - always_live_locals.clone(), + always_live_locals, requires_storage_cursor.into_results(), ); @@ -926,13 +959,12 @@ fn compute_storage_conflicts<'mir, 'tcx>( always_live_locals: BitSet, mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, ) -> BitMatrix { - assert_eq!(body.local_decls.len(), saved_locals.domain_size()); - debug!("compute_storage_conflicts({:?})", body.span); debug!("always_live = {:?}", always_live_locals); // Locals that are always live or ones that need to be stored across // suspension points are not eligible for overlap. + let nr_local_decl_upvars = always_live_locals.domain_size(); let mut ineligible_locals = always_live_locals; ineligible_locals.intersect(&**saved_locals); @@ -940,8 +972,8 @@ fn compute_storage_conflicts<'mir, 'tcx>( let mut visitor = StorageConflictVisitor { body, saved_locals: saved_locals, - local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()), - eligible_storage_live: BitSet::new_empty(body.local_decls.len()), + local_conflicts: BitMatrix::from_row_n(&ineligible_locals, nr_local_decl_upvars), + eligible_storage_live: BitSet::new_empty(nr_local_decl_upvars), }; requires_storage.visit_reachable_with(body, &mut visitor); @@ -955,7 +987,9 @@ fn compute_storage_conflicts<'mir, 'tcx>( // However, in practice these bitsets are not usually large. The layout code // also needs to keep track of how many conflicts each local has, so it's // simpler to keep it this way for now. - let mut storage_conflicts = BitMatrix::new(saved_locals.count(), saved_locals.count()); + let nr_saved_local = saved_locals.count(); + debug!(?nr_saved_local, "compute_storage_conflicts"); + let mut storage_conflicts = BitMatrix::new(nr_saved_local, nr_saved_local); for (saved_local_a, local_a) in saved_locals.iter_enumerated() { if ineligible_locals.contains(local_a) { // Conflicts with everything. @@ -1028,9 +1062,12 @@ impl StorageConflictVisitor<'_, '_, '_> { } } +#[instrument(level = "debug", skip(liveness, body), fields(?body.source))] fn compute_layout<'tcx>( liveness: LivenessInfo, body: &Body<'tcx>, + upvars: IndexVec>, + upvar_spans: &[Span], ) -> ( FxHashMap, VariantIdx, FieldIdx)>, CoroutineLayout<'tcx>, @@ -1043,38 +1080,59 @@ fn compute_layout<'tcx>( storage_conflicts, storage_liveness, } = liveness; + let upvar_start = body.local_decls().next_index(); // Gather live local types and their indices. let mut locals = IndexVec::::new(); let mut tys = IndexVec::::new(); + let mut saved_upvar_start = None; + // `saved_locals` enumerates first locals in the body, then upvars for (saved_local, local) in saved_locals.iter_enumerated() { + assert_ne!(local, ty::CAPTURE_STRUCT_LOCAL); debug!("coroutine saved local {:?} => {:?}", saved_local, local); locals.push(local); - let decl = &body.local_decls[local]; - debug!(?decl); - - // Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared - // the information. This is alright, since `ignore_for_traits` is only relevant when - // this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer - // default. - let ignore_for_traits = match decl.local_info { - // Do not include raw pointers created from accessing `static` items, as those could - // well be re-created by another access to the same static. - ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => { - !is_thread_local - } - // Fake borrows are only read by fake reads, so do not have any reality in - // post-analysis MIR. - ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true, - _ => false, - }; - let decl = - CoroutineSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits }; - debug!(?decl); - tys.push(decl); + if local < upvar_start { + let decl = &body.local_decls[local]; + debug!(?decl); + + // Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared + // the information. This is alright, since `ignore_for_traits` is only relevant when + // this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer + // default. + let ignore_for_traits = match decl.local_info { + // Do not include raw pointers created from accessing `static` items, as those could + // well be re-created by another access to the same static. + ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => { + !is_thread_local + } + // Fake borrows are only read by fake reads, so do not have any reality in + // post-analysis MIR. + ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true, + _ => false, + }; + let decl = + CoroutineSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits }; + debug!(?decl); + + tys.push(decl); + } else { + // This is an upvar + let idx = local.index() - upvar_start.index(); + let ty = upvars[FieldIdx::from_usize(idx)]; + let decl = CoroutineSavedTy { + ty, + source_info: SourceInfo::outermost(upvar_spans[idx]), + ignore_for_traits: false, + }; + let idx = tys.push(decl); + if saved_upvar_start.is_none() { + saved_upvar_start = Some(idx); + } + } } + let saved_upvar_start = saved_upvar_start.unwrap_or(tys.next_index()); // Leave empty variants for the UNRESUMED, RETURNED, and POISONED states. // In debuginfo, these will correspond to the beginning (UNRESUMED) or end @@ -1085,15 +1143,28 @@ fn compute_layout<'tcx>( SourceInfo::outermost(body_span.shrink_to_hi()), SourceInfo::outermost(body_span.shrink_to_hi()), ] - .iter() - .copied() + .into_iter() .collect(); // Build the coroutine variant field list. // Create a map from local indices to coroutine struct indices. - let mut variant_fields: IndexVec> = - iter::repeat(IndexVec::new()).take(RESERVED_VARIANTS).collect(); - let mut remap = FxHashMap::default(); + let variant_fields: [IndexVec; RESERVED_VARIANTS] = [ + // First, the UNRESUMED variant contains all the upvars. + saved_locals + .iter_enumerated() + .filter_map(|(saved_local, local)| (local >= upvar_start).then_some(saved_local)) + .collect(), + IndexVec::new(), + IndexVec::new(), + ]; + let mut variant_fields: IndexVec = variant_fields.into_iter().collect(); + let mut remap: FxHashMap<_, _> = upvars + .iter_enumerated() + .map(|(field, &ty)| { + (upvar_start + field.as_usize(), (ty, VariantIdx::from(UNRESUMED), field)) + }) + .collect(); + debug!(?live_locals_at_suspension_points, "compute_layout"); for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() { let variant_index = VariantIdx::from(RESERVED_VARIANTS + suspension_point_idx); let mut fields = IndexVec::new(); @@ -1104,18 +1175,39 @@ fn compute_layout<'tcx>( // around inside coroutines, so it doesn't matter which variant // index we access them by. let idx = FieldIdx::from_usize(idx); - remap.entry(locals[saved_local]).or_insert((tys[saved_local].ty, variant_index, idx)); + if saved_local.as_usize() < upvar_start.as_usize() { + remap.entry(locals[saved_local]).or_insert(( + tys[saved_local].ty, + variant_index, + idx, + )); + } else { + // An upvar maps into the UNRESUMED state + remap.entry(locals[saved_local]).or_insert(( + tys[saved_local].ty, + VariantIdx::from(UNRESUMED), + FieldIdx::from(saved_local.as_usize() - upvar_start.as_usize()), + )); + } } variant_fields.push(fields); variant_source_info.push(source_info_at_suspension_points[suspension_point_idx]); } + debug!(?remap, "coroutine"); debug!("coroutine variant_fields = {:?}", variant_fields); debug!("coroutine storage_conflicts = {:#?}", storage_conflicts); let mut field_names = IndexVec::from_elem(None, &tys); for var in &body.var_debug_info { let VarDebugInfoContents::Place(place) = &var.value else { continue }; - let Some(local) = place.as_local() else { continue }; + let local = match place.as_ref() { + PlaceRef { + local: ty::CAPTURE_STRUCT_LOCAL, + projection: [ProjectionElem::Field(field, _ty), ..], + } => upvar_start + field.as_usize(), + PlaceRef { local, projection: [] } => local, + _ => continue, + }; let Some(&(_, variant, field)) = remap.get(&local) else { continue }; let saved_local = variant_fields[variant][field]; @@ -1123,6 +1215,7 @@ fn compute_layout<'tcx>( } let layout = CoroutineLayout { + upvar_start: saved_upvar_start, field_tys: tys, field_names, variant_fields, @@ -1220,6 +1313,21 @@ fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { ); } elaborator.patch.apply(body); + for block_data in body.basic_blocks_mut() { + if let Terminator { + source_info: _, + kind: TerminatorKind::Drop { place, target: _, unwind: _, replace: _ }, + } = block_data.terminator_mut() + && SELF_ARG == place.local + && matches!(**place.projection, [PlaceElem::Field(..), ..]) + { + let more_projections = &**place.projection; + assert!(!more_projections.is_empty()); + *place = tcx + .mk_place_downcast_unnamed(Place::from(SELF_ARG), VariantIdx::from(UNRESUMED)) + .project_deeper(more_projections, tcx); + } + } } fn create_coroutine_drop_shim<'tcx>( @@ -1248,6 +1356,7 @@ fn create_coroutine_drop_shim<'tcx>( insert_switch(&mut body, cases, transform, TerminatorKind::Return); + // All coroutine drop exits are now returns for block in body.basic_blocks_mut() { let kind = &mut block.terminator_mut().kind; if let TerminatorKind::CoroutineDrop = *kind { @@ -1276,7 +1385,22 @@ fn create_coroutine_drop_shim<'tcx>( // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible // filename. body.source.instance = coroutine_instance; - dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(())); + // Select appropriate flavour of the coroutine kind shim. + let ty::Coroutine(_, args) = coroutine_ty.kind() else { bug!() }; + let pass_name = { + let kind_ty = args.as_coroutine().kind_ty(); + if let ty::Int(..) = kind_ty.kind() + && let Some(kind) = kind_ty.to_opt_closure_kind() + { + match kind { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => "coroutine_drop_fnmut", + ty::ClosureKind::FnOnce => "coroutine_drop_fnonce", + } + } else { + "coroutine_drop" + } + }; + dump_mir(tcx, false, pass_name, &0 as _, &body, |_, _| Ok(())); body.source.instance = drop_instance; body @@ -1462,7 +1586,7 @@ fn create_coroutine_resume_function<'tcx>( pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None); - dump_mir(tcx, false, "coroutine_resume", &0, body, |_, _| Ok(())); + dump_mir(tcx, false, "coroutine_resume", &0 as _, body, |_, _| Ok(())); } fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock { @@ -1527,7 +1651,7 @@ fn create_cases<'tcx>( } } - if operation == Operation::Resume { + if matches!(operation, Operation::Resume) { // Move the resume argument to the destination place of the `Yield` terminator let resume_arg = Local::new(2); // 0 = return, 1 = self statements.push(Statement { @@ -1567,21 +1691,33 @@ pub(crate) fn mir_coroutine_witnesses<'tcx>( // The first argument is the coroutine type passed by value let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; - let movable = match *coroutine_ty.kind() { - ty::Coroutine(def_id, _) => tcx.coroutine_movability(def_id) == hir::Movability::Movable, + let (def_id, upvar_tys, movable) = match *coroutine_ty.kind() { + ty::Coroutine(def_id, args) => ( + def_id, + args.as_coroutine().upvar_tys(), + matches!(tcx.coroutine_movability(def_id), hir::Movability::Movable), + ), ty::Error(_) => return None, _ => span_bug!(body.span, "unexpected coroutine type {}", coroutine_ty), }; // The witness simply contains all locals live across suspend points. + dump_mir(tcx, true, "coroutine_liveness", &"witness", body, |_, _| Ok(())); let always_live_locals = always_storage_live_locals(body); - let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); + let upvars: IndexVec = upvar_tys.iter().collect(); + let upvar_spans: SmallVec<[_; 4]> = tcx + .closure_captures(def_id.expect_local()) + .iter() + .map(|capture| capture.var_ident.span) + .collect(); + let liveness_info = + locals_live_across_suspend_points(tcx, body, &always_live_locals, movable, &upvars); // Extract locals which are live across suspension point into `layout` // `remap` gives a mapping from local indices onto coroutine struct indices // `storage_liveness` tells us which locals have live storage at suspension points - let (_, coroutine_layout, _) = compute_layout(liveness_info, body); + let (_, coroutine_layout, _) = compute_layout(liveness_info, body, upvars, &upvar_spans); check_suspend_tys(tcx, &coroutine_layout, body); @@ -1599,19 +1735,30 @@ impl<'tcx> MirPass<'tcx> for StateTransform { assert!(body.coroutine_drop().is_none()); // The first argument is the coroutine type passed by value - let coroutine_ty = body.local_decls.raw[1].ty; + let coroutine_ty = body.local_decls[SELF_ARG].ty; let coroutine_kind = body.coroutine_kind().unwrap(); // Get the discriminant type and args which typeck computed - let (discr_ty, movable) = match *coroutine_ty.kind() { - ty::Coroutine(_, args) => { + let (def_id, discr_ty, upvar_tys, movable) = match *coroutine_ty.kind() { + ty::Coroutine(def_id, args) => { let args = args.as_coroutine(); - (args.discr_ty(tcx), coroutine_kind.movability() == hir::Movability::Movable) + ( + def_id, + args.discr_ty(tcx), + args.upvar_tys(), + matches!(coroutine_kind.movability(), hir::Movability::Movable), + ) } _ => { tcx.dcx().span_bug(body.span, format!("unexpected coroutine type {coroutine_ty}")); } }; + let upvars: IndexVec = upvar_tys.iter().collect(); + let upvar_spans: SmallVec<[_; 4]> = tcx + .closure_captures(def_id.expect_local()) + .iter() + .map(|capture| capture.var_ident.span) + .collect(); let new_ret_ty = match coroutine_kind { CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => { @@ -1675,11 +1822,12 @@ impl<'tcx> MirPass<'tcx> for StateTransform { ))), }, ); + dump_mir(tcx, false, "coroutine_post-replace-resume", &0 as _, body, |_, _| Ok(())); let always_live_locals = always_storage_live_locals(body); let liveness_info = - locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); + locals_live_across_suspend_points(tcx, body, &always_live_locals, movable, &upvars); if tcx.sess.opts.unstable_opts.validate_mir { let mut vis = EnsureCoroutineFieldAssignmentsNeverAlias { @@ -1694,7 +1842,8 @@ impl<'tcx> MirPass<'tcx> for StateTransform { // Extract locals which are live across suspension point into `layout` // `remap` gives a mapping from local indices onto coroutine struct indices // `storage_liveness` tells us which locals have live storage at suspension points - let (remap, layout, storage_liveness) = compute_layout(liveness_info, body); + let (remap, layout, storage_liveness) = + compute_layout(liveness_info, body, upvars, &upvar_spans); let can_return = can_return(tcx, body, tcx.param_env(body.source.def_id())); @@ -1714,6 +1863,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { discr_ty, old_ret_ty, old_yield_ty, + upvar_start: body.local_decls.next_index(), }; transform.visit_body(body); @@ -1742,14 +1892,14 @@ impl<'tcx> MirPass<'tcx> for StateTransform { // This is expanded to a drop ladder in `elaborate_coroutine_drops`. let drop_clean = insert_clean_drop(body); - dump_mir(tcx, false, "coroutine_pre-elab", &0, body, |_, _| Ok(())); + dump_mir(tcx, false, "coroutine_pre-elab", &0 as _, body, |_, _| Ok(())); // Expand `drop(coroutine_struct)` to a drop ladder which destroys upvars. - // If any upvars are moved out of, drop elaboration will handle upvar destruction. + // If any upvars are moved out of, earlier rounds of drop elaboration will handle upvar destruction. // However we need to also elaborate the code generated by `insert_clean_drop`. elaborate_coroutine_drops(tcx, body); - dump_mir(tcx, false, "coroutine_post-transform", &0, body, |_, _| Ok(())); + dump_mir(tcx, false, "coroutine_post-transform", &0 as _, body, |_, _| Ok(())); // Create a copy of our MIR and use it to create the drop shim for the coroutine let drop_shim = create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean); diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index ccb229616e855..b37a849d34b21 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -223,6 +223,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { ); let mut by_move_body = body.clone(); + by_move_body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty = by_move_coroutine_ty; MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); // FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body. diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index e6317e5469ce8..fdf2b6a78558b 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -69,7 +69,8 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } // Account that `arg` is read from, so we don't promote another argument to a move. - LivenessTransferFunction(&mut state).visit_operand(arg, loc); + LivenessTransferFunction { trans: &mut state, upvars: None } + .visit_operand(arg, loc); } } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 10fea09531a65..f6515b5cf8158 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -169,7 +169,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body); - let live = MaybeLiveLocals + let live = MaybeLiveLocals { upvars: None } .into_engine(tcx, body) .pass_name("MaybeLiveLocals-DestinationPropagation") .iterate_to_fixpoint(); diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index fa6906bdd554f..fb3ad1ea0003b 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -26,9 +26,8 @@ pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } +#[instrument(level = "debug", skip(tcx))] fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'tcx> { - debug!("make_shim({:?})", instance); - let mut result = match instance { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), ty::InstanceDef::VTableShim(def_id) => { @@ -206,9 +205,8 @@ fn local_decls_for_sig<'tcx>( .collect() } +#[instrument(level = "debug", skip(tcx))] fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) -> Body<'tcx> { - debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); - assert!(!matches!(ty, Some(ty) if ty.is_coroutine())); let args = if let Some(ty) = ty { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 2067956d0f5f0..fc6e145a91672 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2414,8 +2414,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let Some(coroutine_info) = self.tcx.mir_coroutine_witnesses(coroutine_did) { debug!(?coroutine_info); - 'find_source: for (variant, source_info) in - coroutine_info.variant_fields.iter().zip(&coroutine_info.variant_source_info) + // The first variant is a collection of upvars, which is handled below + 'find_source: for (variant, source_info) in coroutine_info + .variant_fields + .iter() + .zip(&coroutine_info.variant_source_info) + .skip(1) { debug!(?variant); for &local in variant { @@ -3203,8 +3207,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); let nested_ty = parent_trait_ref.skip_binder().self_ty(); - matches!(nested_ty.kind(), ty::Coroutine(..)) - || matches!(nested_ty.kind(), ty::Closure(..)) + matches!(nested_ty.kind(), ty::Coroutine(..) | ty::Closure(..)) } else { false } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 77cd4662ae575..18f6ca8a74af2 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -746,12 +746,14 @@ fn coroutine_saved_local_eligibility( // Next, check every pair of eligible locals to see if they // conflict. for local_a in info.storage_conflicts.rows() { + debug!(?local_a, "coroutine_saved_local_eligibility"); let conflicts_a = info.storage_conflicts.count(local_a); if ineligible_locals.contains(local_a) { continue; } for local_b in info.storage_conflicts.iter(local_a) { + debug!(?local_b, "coroutine_saved_local_eligibility"); // local_a and local_b are storage live at the same time, therefore they // cannot overlap in the coroutine layout. The only way to guarantee // this is if they are in the same variant, or one is ineligible @@ -771,6 +773,7 @@ fn coroutine_saved_local_eligibility( trace!("removing local {:?} due to conflict with {:?}", remove, other); } } + debug!("coroutine_saved_local_eligibility A"); // Count the number of variants in use. If only one of them, then it is // impossible to overlap any locals in our layout. In this case it's @@ -804,6 +807,7 @@ fn coroutine_saved_local_eligibility( } /// Compute the full coroutine layout. +#[instrument(level = "debug", skip(cx))] fn coroutine_layout<'tcx>( cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, ty: Ty<'tcx>, @@ -822,7 +826,7 @@ fn coroutine_layout<'tcx>( // Build a prefix layout, including "promoting" all ineligible // locals as part of the prefix. We compute the layout of all of // these fields at once to get optimal packing. - let tag_index = args.as_coroutine().prefix_tys().len(); + let tag_index = 0; // `info.variant_fields` already accounts for the reserved variants, so no need to add them. let max_discr = (info.variant_fields.len() - 1) as u128; @@ -838,14 +842,8 @@ fn coroutine_layout<'tcx>( let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty); Ok(cx.spanned_layout_of(uninit_ty, info.field_tys[local].source_info.span)?.layout) }); - let prefix_layouts = args - .as_coroutine() - .prefix_tys() - .iter() - .map(|ty| Ok(cx.layout_of(ty)?.layout)) - .chain(iter::once(Ok(tag_layout))) - .chain(promoted_layouts) - .try_collect::>()?; + let prefix_layouts: IndexVec<_, _> = + [Ok(tag_layout)].into_iter().chain(promoted_layouts).try_collect()?; let prefix = univariant_uninterned( cx, ty, @@ -900,6 +898,7 @@ fn coroutine_layout<'tcx>( .iter_enumerated() .map(|(index, variant_fields)| { // Only include overlap-eligible fields when we compute our variant layout. + // Each variant will actually store `MaybeUninit`s. let variant_only_tys = variant_fields .iter() .filter(|local| match assignments[**local] { @@ -908,16 +907,16 @@ fn coroutine_layout<'tcx>( Assigned(_) => bug!("assignment does not match variant"), Ineligible(_) => false, }) - .map(|local| { - let field_ty = instantiate_field(info.field_tys[*local].ty); - Ty::new_maybe_uninit(tcx, field_ty) + .map(|&local| { + let field_ty = instantiate_field(info.field_tys[local].ty); + (Ty::new_maybe_uninit(tcx, field_ty), info.field_tys[local].source_info.span) }); let mut variant = univariant_uninterned( cx, ty, &variant_only_tys - .map(|ty| Ok(cx.layout_of(ty)?.layout)) + .map(|(ty, span)| Ok(cx.spanned_layout_of(ty, span)?.layout)) .try_collect::>()?, &ReprOptions::default(), StructKind::Prefixed(prefix_size, prefix_align.abi), @@ -1135,36 +1134,11 @@ fn variant_info_for_coroutine<'tcx>( def_id: DefId, args: ty::GenericArgsRef<'tcx>, ) -> (Vec, Option) { - use itertools::Itertools; - let Variants::Multiple { tag, ref tag_encoding, tag_field, .. } = layout.variants else { return (vec![], None); }; let coroutine = cx.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap(); - let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); - - let mut upvars_size = Size::ZERO; - let upvar_fields: Vec<_> = args - .as_coroutine() - .upvar_tys() - .iter() - .zip_eq(upvar_names) - .enumerate() - .map(|(field_idx, (_, name))| { - let field_layout = layout.field(cx, field_idx); - let offset = layout.fields.offset(field_idx); - upvars_size = upvars_size.max(offset + field_layout.size); - FieldInfo { - kind: FieldKind::Upvar, - name: *name, - offset: offset.bytes(), - size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), - type_name: None, - } - }) - .collect(); let mut variant_infos: Vec<_> = coroutine .variant_fields @@ -1196,14 +1170,8 @@ fn variant_info_for_coroutine<'tcx>( .then(|| Symbol::intern(&field_layout.ty.to_string())), } }) - .chain(upvar_fields.iter().copied()) .collect(); - // If the variant has no state-specific fields, then it's the size of the upvars. - if variant_size == Size::ZERO { - variant_size = upvars_size; - } - // This `if` deserves some explanation. // // The layout code has a choice of where to place the discriminant of this coroutine. diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index f25a474d9bbd6..d22b2b06ca842 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -6,6 +6,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::CoroutineLayout; +use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::{sym, Span}; @@ -210,48 +211,90 @@ impl<'tcx> LateLintPass<'tcx> for AwaitHolding { impl AwaitHolding { fn check_interior_types(&self, cx: &LateContext<'_>, coroutine: &CoroutineLayout<'_>) { for (ty_index, ty_cause) in coroutine.field_tys.iter_enumerated() { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if let ty::Adt(adt, _) = ty_cause.ty.kind() { let await_points = || { coroutine .variant_source_info .iter_enumerated() .filter_map(|(variant, source_info)| { - coroutine.variant_fields[variant] - .raw - .contains(&ty_index) - .then_some(source_info.span) + let is_upvar = variant.as_usize() == ty::CoroutineArgs::UNRESUMED; + coroutine.variant_fields[variant].raw.contains(&ty_index).then(|| { + if is_upvar { + (is_upvar, coroutine.field_tys[ty_index].source_info.span) + } else { + (is_upvar, source_info.span) + } + }) }) .collect::>() }; if is_mutex_guard(cx, adt.did()) { + let occurrences = await_points(); + let is_upvar = occurrences.iter().any(|&(is_upvar, _)| is_upvar); + let sp: Vec<_> = occurrences + .into_iter() + .filter_map(|(is_upvar, sp)| (!is_upvar).then_some(sp)) + .collect(); span_lint_and_then( cx, AWAIT_HOLDING_LOCK, ty_cause.source_info.span, - "this `MutexGuard` is held across an `await` point", + if is_upvar { + "this `MutexGuard` is passed in through an argument or captured by the closure body" + } else { + "this `MutexGuard` is held across an `await` point" + }, |diag| { - diag.help( + diag.help(if is_upvar { + "consider using an async-aware `Mutex` type" + } else { "consider using an async-aware `Mutex` type or ensuring the \ - `MutexGuard` is dropped before calling await", - ); - diag.span_note( - await_points(), - "these are all the `await` points this lock is held through", - ); + `MutexGuard` is dropped before calling await" + }); + if !sp.is_empty() { + diag.span_note( + sp, + if is_upvar { + "these are the arguments or captured variables holding the `MutexGuard`" + } else { + "these are all the `await` points this lock is held through" + }, + ); + } }, ); } else if is_refcell_ref(cx, adt.did()) { + let occurrences = await_points(); + let is_upvar = occurrences.iter().any(|&(is_upvar, _)| is_upvar); + let sp: Vec<_> = occurrences + .into_iter() + .filter_map(|(is_upvar, sp)| (!is_upvar).then_some(sp)) + .collect(); span_lint_and_then( cx, AWAIT_HOLDING_REFCELL_REF, ty_cause.source_info.span, - "this `RefCell` reference is held across an `await` point", + if is_upvar { + "this `RefCell` reference is passed in through an argument or captured by the closure body" + } else { + "this `RefCell` reference is held across an `await` point" + }, |diag| { - diag.help("ensure the reference is dropped before calling `await`"); - diag.span_note( - await_points(), - "these are all the `await` points this reference is held through", - ); + diag.help(if is_upvar { + "ensure the `RefCell` reference is not passed as an argument, captured or kept before calling `await`" + } else { + "ensure the reference is dropped before calling `await`" + }); + if !sp.is_empty() { + diag.span_note( + sp, + if is_upvar { + "these are all the arguments or captured variables holding this reference" + } else { + "these are all the `await` points this reference is held through" + }, + ); + } }, ); } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { diff --git a/src/tools/clippy/tests/ui/await_holding_lock.rs b/src/tools/clippy/tests/ui/await_holding_lock.rs index 8e5510e6cd01c..9aa4b8c913418 100644 --- a/src/tools/clippy/tests/ui/await_holding_lock.rs +++ b/src/tools/clippy/tests/ui/await_holding_lock.rs @@ -4,7 +4,7 @@ // When adding or modifying a test, please do the same for parking_lot::Mutex. mod std_mutex { use super::baz; - use std::sync::{Mutex, RwLock}; + use std::sync::{Mutex, MutexGuard, RwLock}; pub async fn bad(x: &Mutex) -> u32 { let guard = x.lock().unwrap(); @@ -61,6 +61,11 @@ mod std_mutex { first + second + third } + pub async fn equally_bad(x: MutexGuard<'_, u32>) { + //~^ ERROR: this `MutexGuard` is passed in through an argument or captured by the closure body + drop(x); + } + pub async fn not_good(x: &Mutex) -> u32 { let first = baz().await; diff --git a/src/tools/clippy/tests/ui/await_holding_lock.stderr b/src/tools/clippy/tests/ui/await_holding_lock.stderr index 0af48a36acca1..b6f28b3dbb330 100644 --- a/src/tools/clippy/tests/ui/await_holding_lock.stderr +++ b/src/tools/clippy/tests/ui/await_holding_lock.stderr @@ -55,80 +55,88 @@ LL | LL | let third = baz().await; | ^^^^^ +error: this `MutexGuard` is passed in through an argument or captured by the closure body + --> tests/ui/await_holding_lock.rs:64:30 + | +LL | pub async fn equally_bad(x: MutexGuard<'_, u32>) { + | ^ + | + = help: consider using an async-aware `Mutex` type + error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:68:17 + --> tests/ui/await_holding_lock.rs:73:17 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:70:19 + --> tests/ui/await_holding_lock.rs:75:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:81:17 + --> tests/ui/await_holding_lock.rs:86:17 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:83:19 + --> tests/ui/await_holding_lock.rs:88:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:94:13 + --> tests/ui/await_holding_lock.rs:99:13 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:96:15 + --> tests/ui/await_holding_lock.rs:101:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:110:13 + --> tests/ui/await_holding_lock.rs:115:13 | LL | let guard = x.read(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:112:15 + --> tests/ui/await_holding_lock.rs:117:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:116:13 + --> tests/ui/await_holding_lock.rs:121:13 | LL | let mut guard = x.write(); | ^^^^^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:118:15 + --> tests/ui/await_holding_lock.rs:123:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:138:13 + --> tests/ui/await_holding_lock.rs:143:13 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:141:28 + --> tests/ui/await_holding_lock.rs:146:28 | LL | let second = baz().await; | ^^^^^ @@ -137,43 +145,43 @@ LL | let third = baz().await; | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:152:17 + --> tests/ui/await_holding_lock.rs:157:17 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:154:19 + --> tests/ui/await_holding_lock.rs:159:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:165:17 + --> tests/ui/await_holding_lock.rs:170:17 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:167:19 + --> tests/ui/await_holding_lock.rs:172:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:186:9 + --> tests/ui/await_holding_lock.rs:191:9 | LL | let mut guard = x.lock().unwrap(); | ^^^^^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:190:11 + --> tests/ui/await_holding_lock.rs:195:11 | LL | baz().await; | ^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs b/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs index 5bd26c6283627..8adcd6d9a3bcd 100644 --- a/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs +++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs @@ -1,6 +1,6 @@ #![warn(clippy::await_holding_refcell_ref)] -use std::cell::RefCell; +use std::cell::{Ref, RefCell}; async fn bad(x: &RefCell) -> u32 { let b = x.borrow(); @@ -41,6 +41,10 @@ async fn also_bad(x: &RefCell) -> u32 { first + second + third } +async fn equally_bad(x: Ref<'_, u32>) { + //~^ ERROR: this `RefCell` reference is passed in through an argument or captured +} + async fn less_bad(x: &RefCell) -> u32 { let first = baz().await; diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr index 6b474c27ddc18..5b0a37612ebce 100644 --- a/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr +++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr @@ -42,44 +42,52 @@ LL | LL | let third = baz().await; | ^^^^^ +error: this `RefCell` reference is passed in through an argument or captured by the closure body + --> tests/ui/await_holding_refcell_ref.rs:44:22 + | +LL | async fn equally_bad(x: Ref<'_, u32>) { + | ^ + | + = help: ensure the `RefCell` reference is not passed as an argument, captured or kept before calling `await` + error: this `RefCell` reference is held across an `await` point - --> tests/ui/await_holding_refcell_ref.rs:47:9 + --> tests/ui/await_holding_refcell_ref.rs:51:9 | LL | let b = x.borrow_mut(); | ^ | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> tests/ui/await_holding_refcell_ref.rs:50:24 + --> tests/ui/await_holding_refcell_ref.rs:54:24 | LL | let second = baz().await; | ^^^^^ error: this `RefCell` reference is held across an `await` point - --> tests/ui/await_holding_refcell_ref.rs:63:13 + --> tests/ui/await_holding_refcell_ref.rs:67:13 | LL | let b = x.borrow_mut(); | ^ | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> tests/ui/await_holding_refcell_ref.rs:65:15 + --> tests/ui/await_holding_refcell_ref.rs:69:15 | LL | baz().await | ^^^^^ error: this `RefCell` reference is held across an `await` point - --> tests/ui/await_holding_refcell_ref.rs:76:13 + --> tests/ui/await_holding_refcell_ref.rs:80:13 | LL | let b = x.borrow_mut(); | ^ | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> tests/ui/await_holding_refcell_ref.rs:78:15 + --> tests/ui/await_holding_refcell_ref.rs:82:15 | LL | baz().await | ^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.stderr index 67677d6367aaa..15148a086f30b 100644 --- a/src/tools/clippy/tests/ui/future_not_send.stderr +++ b/src/tools/clippy/tests/ui/future_not_send.stderr @@ -13,11 +13,14 @@ LL | LL | async { true }.await | ^^^^^ await occurs here, with `rc` maybe used later = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` -note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> tests/ui/future_not_send.rs:7:39 +note: future is not `Send` as this value is used across an await + --> tests/ui/future_not_send.rs:9:20 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ^^^^ has type `&std::cell::Cell` which is not `Send`, because `std::cell::Cell` is not `Sync` + | ---- has type `&std::cell::Cell` which is not `Send` +LL | +LL | async { true }.await + | ^^^^^ await occurs here, with `cell` maybe used later = note: `std::cell::Cell` doesn't implement `std::marker::Sync` = note: `-D clippy::future-not-send` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]` @@ -92,11 +95,14 @@ error: future cannot be sent between threads safely LL | pub async fn public_future(&self) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send` | -note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> tests/ui/future_not_send.rs:44:32 +note: future is not `Send` as this value is used across an await + --> tests/ui/future_not_send.rs:46:31 | LL | pub async fn public_future(&self) { - | ^^^^^ has type `&Dummy` which is not `Send`, because `Dummy` is not `Sync` + | ---- has type `&Dummy` which is not `Send` +LL | +LL | self.private_future().await; + | ^^^^^ await occurs here, with `self` maybe used later = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely diff --git a/src/tools/clippy/tests/ui/large_futures.fixed b/src/tools/clippy/tests/ui/large_futures.fixed index aa8c3021b9708..c6a8da62ba2a6 100644 --- a/src/tools/clippy/tests/ui/large_futures.fixed +++ b/src/tools/clippy/tests/ui/large_futures.fixed @@ -33,7 +33,7 @@ pub async fn test() { Box::pin(foo()).await; //~^ ERROR: large future with a size of 65540 bytes Box::pin(calls_fut(fut)).await; - //~^ ERROR: large future with a size of 49159 bytes + //~^ ERROR: large future with a size of 32774 bytes } pub fn foo() -> impl std::future::Future { diff --git a/src/tools/clippy/tests/ui/large_futures.rs b/src/tools/clippy/tests/ui/large_futures.rs index fc6ea458d3dbd..c2b1e1697fd21 100644 --- a/src/tools/clippy/tests/ui/large_futures.rs +++ b/src/tools/clippy/tests/ui/large_futures.rs @@ -33,7 +33,7 @@ pub async fn test() { foo().await; //~^ ERROR: large future with a size of 65540 bytes calls_fut(fut).await; - //~^ ERROR: large future with a size of 49159 bytes + //~^ ERROR: large future with a size of 32774 bytes } pub fn foo() -> impl std::future::Future { diff --git a/src/tools/clippy/tests/ui/large_futures.stderr b/src/tools/clippy/tests/ui/large_futures.stderr index 5709c7b77a0a5..cf35400d3c676 100644 --- a/src/tools/clippy/tests/ui/large_futures.stderr +++ b/src/tools/clippy/tests/ui/large_futures.stderr @@ -31,7 +31,7 @@ error: large future with a size of 65540 bytes LL | foo().await; | ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())` -error: large future with a size of 49159 bytes +error: large future with a size of 32774 bytes --> tests/ui/large_futures.rs:35:5 | LL | calls_fut(fut).await; diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index d1787b35abd23..8faedebadba14 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -533,6 +533,7 @@ mod issue5787 { impl Foo { // doesn't get linted without async + #[allow(clippy::await_holding_lock)] pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { guard } diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index 03d6f2013586c..112ecec08d52d 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -533,6 +533,7 @@ mod issue5787 { impl Foo { // doesn't get linted without async + #[allow(clippy::await_holding_lock)] pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { guard } diff --git a/tests/debuginfo/coroutine-objects.rs b/tests/debuginfo/coroutine-objects.rs index 9e1bd5d62e7a1..f696b2e615122 100644 --- a/tests/debuginfo/coroutine-objects.rs +++ b/tests/debuginfo/coroutine-objects.rs @@ -11,16 +11,16 @@ // gdb-command:run // gdb-command:print b -// gdb-check:$1 = coroutine_objects::main::{coroutine_env#0}::Unresumed{_ref__a: 0x[...]} +// gdb-check:$1 = coroutine_objects::main::{coroutine_env#0}::Unresumed{a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$2 = coroutine_objects::main::{coroutine_env#0}::Suspend0{c: 6, d: 7, _ref__a: 0x[...]} +// gdb-check:$2 = coroutine_objects::main::{coroutine_env#0}::Suspend0{c: 6, d: 7, a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$3 = coroutine_objects::main::{coroutine_env#0}::Suspend1{c: 7, d: 8, _ref__a: 0x[...]} +// gdb-check:$3 = coroutine_objects::main::{coroutine_env#0}::Suspend1{c: 7, d: 8, a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$4 = coroutine_objects::main::{coroutine_env#0}::Returned{_ref__a: 0x[...]} +// gdb-check:$4 = coroutine_objects::main::{coroutine_env#0}::Returned // === LLDB TESTS ================================================================================== diff --git a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir index 7480324b17791..dabc545f7c240 100644 --- a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir @@ -1,12 +1,28 @@ // MIR for `a::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { + upvar_start: _0, field_tys: {}, variant_fields: { Unresumed(0): [], Returned (1): [], Panicked (2): [], }, + field_names: {}, storage_conflicts: BitMatrix(0x0) {}, + variant_source_info: { + 0: SourceInfo { + span: $DIR/async_await.rs:12:14: 12:14 (#0), + scope: scope[0], + }, + 1: SourceInfo { + span: $DIR/async_await.rs:12:16: 12:16 (#0), + scope: scope[0], + }, + 2: SourceInfo { + span: $DIR/async_await.rs:12:16: 12:16 (#0), + scope: scope[0], + }, + }, } */ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index d2a0fb0cb3c3d..daa8dfd0b6bd9 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -1,5 +1,6 @@ // MIR for `b::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { + upvar_start: _2, field_tys: { _0: CoroutineSavedTy { ty: Coroutine( @@ -45,10 +46,40 @@ Suspend0 (3): [_0], Suspend1 (4): [_1], }, + field_names: { + _0: Some( + "__awaitee", + ), + _1: Some( + "__awaitee", + ), + }, storage_conflicts: BitMatrix(2x2) { (_0, _0), (_1, _1), }, + variant_source_info: { + 0: SourceInfo { + span: $DIR/async_await.rs:15:18: 15:18 (#0), + scope: scope[0], + }, + 1: SourceInfo { + span: $DIR/async_await.rs:18:2: 18:2 (#0), + scope: scope[0], + }, + 2: SourceInfo { + span: $DIR/async_await.rs:18:2: 18:2 (#0), + scope: scope[0], + }, + 3: SourceInfo { + span: $DIR/async_await.rs:16:9: 16:14 (#8), + scope: scope[1], + }, + 4: SourceInfo { + span: $DIR/async_await.rs:17:9: 17:14 (#10), + scope: scope[3], + }, + }, } */ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> Poll<()> { diff --git a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir index 165aa3a05cb7d..03ea4ea1aa2fb 100644 --- a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir @@ -1,5 +1,6 @@ // MIR for `main::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { + upvar_start: _1, field_tys: { _0: CoroutineSavedTy { ty: HasDrop, @@ -16,9 +17,32 @@ Panicked (2): [], Suspend0 (3): [_0], }, + field_names: { + _0: Some( + "_d", + ), + }, storage_conflicts: BitMatrix(1x1) { (_0, _0), }, + variant_source_info: { + 0: SourceInfo { + span: $DIR/coroutine_tiny.rs:20:16: 20:16 (#0), + scope: scope[0], + }, + 1: SourceInfo { + span: $DIR/coroutine_tiny.rs:26:6: 26:6 (#0), + scope: scope[0], + }, + 2: SourceInfo { + span: $DIR/coroutine_tiny.rs:26:6: 26:6 (#0), + scope: scope[0], + }, + 3: SourceInfo { + span: $DIR/coroutine_tiny.rs:23:13: 23:18 (#0), + scope: scope[1], + }, + }, } */ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:20:16: 20:24}>, _2: u8) -> CoroutineState<(), ()> { diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index f8cceacd7e694..1e72f757e11c4 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -22,7 +22,7 @@ } + scope 6 (inlined ActionPermit::<'_, T>::perform::{closure#0}) { + debug _task_context => _31; -+ debug self => ((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})).0: ActionPermit<'_, T>); ++ debug self => (((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})) as variant#0).0: ActionPermit<'_, T>); + let _11: ActionPermit<'_, T>; + let mut _12: std::future::Ready<()>; + let mut _13: std::future::Ready<()>; @@ -162,7 +162,7 @@ + _31 = move _9; + _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move (((*_35) as variant#0).0: ActionPermit<'_, T>); + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index fd080d22d3ad7..84a55a9a61878 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -22,7 +22,7 @@ } + scope 6 (inlined ActionPermit::<'_, T>::perform::{closure#0}) { + debug _task_context => _31; -+ debug self => ((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})).0: ActionPermit<'_, T>); ++ debug self => (((*(_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()})) as variant#0).0: ActionPermit<'_, T>); + let _11: ActionPermit<'_, T>; + let mut _12: std::future::Ready<()>; + let mut _13: std::future::Ready<()>; @@ -179,7 +179,7 @@ + _31 = move _9; + _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move (((*_35) as variant#0).0: ActionPermit<'_, T>); + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); diff --git a/tests/ui/async-await/awaiting-unsized-param.rs b/tests/ui/async-await/awaiting-unsized-param.rs index 45611eae41f5c..b00b23c1139ee 100644 --- a/tests/ui/async-await/awaiting-unsized-param.rs +++ b/tests/ui/async-await/awaiting-unsized-param.rs @@ -7,6 +7,7 @@ use std::future::Future; async fn bug(mut f: dyn Future + Unpin) -> T { //~^ ERROR the size for values of type `(dyn Future + Unpin + 'static)` cannot be known at compilation time + //~| ERROR the size for values of type `dyn Future + Unpin` cannot be known at compilation time (&mut f).await } diff --git a/tests/ui/async-await/awaiting-unsized-param.stderr b/tests/ui/async-await/awaiting-unsized-param.stderr index 0104736976d5d..4095ca21007b3 100644 --- a/tests/ui/async-await/awaiting-unsized-param.stderr +++ b/tests/ui/async-await/awaiting-unsized-param.stderr @@ -16,6 +16,15 @@ LL | async fn bug(mut f: dyn Future + Unpin) -> T { = help: the trait `Sized` is not implemented for `(dyn Future + Unpin + 'static)` = note: all values captured by value by a closure must have a statically known size -error: aborting due to 1 previous error; 1 warning emitted +error[E0277]: the size for values of type `dyn Future + Unpin` cannot be known at compilation time + --> $DIR/awaiting-unsized-param.rs:8:21 + | +LL | async fn bug(mut f: dyn Future + Unpin) -> T { + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Future + Unpin` + = note: all values live across `await` must have a statically known size + +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. 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 def967ba195ef..f87db77f0c754 100644 --- a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout +++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout @@ -1,41 +1,35 @@ -print-type-size type: `{async fn body of test()}`: 3078 bytes, alignment: 1 bytes +print-type-size type: `{async fn body of test()}`: 2053 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes -print-type-size variant `Suspend0`: 3077 bytes -print-type-size local `.__awaitee`: 3077 bytes, type: {async fn body of calls_fut<{async fn body of big_fut()}>()} +print-type-size variant `Suspend0`: 2052 bytes +print-type-size local `.__awaitee`: 2052 bytes, type: {async fn body of calls_fut<{async fn body of big_fut()}>()} print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes -print-type-size type: `std::mem::ManuallyDrop<{async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 3077 bytes, alignment: 1 bytes -print-type-size field `.value`: 3077 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 3077 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 3077 bytes +print-type-size type: `std::mem::ManuallyDrop<{async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 2052 bytes, alignment: 1 bytes +print-type-size field `.value`: 2052 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 2052 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 2052 bytes print-type-size field `.uninit`: 0 bytes -print-type-size field `.value`: 3077 bytes -print-type-size type: `{async fn body of calls_fut<{async fn body of big_fut()}>()}`: 3077 bytes, alignment: 1 bytes +print-type-size field `.value`: 2052 bytes +print-type-size type: `{async fn body of calls_fut<{async fn body of big_fut()}>()}`: 2052 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes -print-type-size variant `Unresumed`: 1025 bytes -print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes -print-type-size variant `Suspend0`: 2052 bytes -print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes -print-type-size padding: 1 bytes +print-type-size variant `Unresumed`: 2051 bytes +print-type-size padding: 1026 bytes print-type-size local `.fut`: 1025 bytes, alignment: 1 bytes +print-type-size variant `Suspend0`: 1027 bytes +print-type-size local `.fut`: 1025 bytes print-type-size local `..coroutine_field4`: 1 bytes, type: bool print-type-size local `.__awaitee`: 1 bytes, type: {async fn body of wait()} -print-type-size variant `Suspend1`: 3076 bytes -print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes -print-type-size padding: 1026 bytes +print-type-size variant `Suspend1`: 2051 bytes +print-type-size padding: 1025 bytes print-type-size local `..coroutine_field4`: 1 bytes, alignment: 1 bytes, type: bool print-type-size local `.__awaitee`: 1025 bytes, type: {async fn body of big_fut()} -print-type-size variant `Suspend2`: 2052 bytes -print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes -print-type-size padding: 1 bytes -print-type-size local `.fut`: 1025 bytes, alignment: 1 bytes +print-type-size variant `Suspend2`: 1027 bytes +print-type-size local `.fut`: 1025 bytes print-type-size local `..coroutine_field4`: 1 bytes, type: bool print-type-size local `.__awaitee`: 1 bytes, type: {async fn body of wait()} -print-type-size variant `Returned`: 1025 bytes -print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes -print-type-size variant `Panicked`: 1025 bytes -print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body of big_fut()}>`: 1025 bytes, alignment: 1 bytes print-type-size field `.value`: 1025 bytes print-type-size type: `std::mem::MaybeUninit<{async fn body of big_fut()}>`: 1025 bytes, alignment: 1 bytes @@ -45,11 +39,15 @@ print-type-size field `.value`: 1025 bytes print-type-size type: `{async fn body of big_fut()}`: 1025 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 1024 bytes -print-type-size upvar `.arg`: 1024 bytes -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 local `.arg`: 1024 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<[u8; 1024]>`: 1024 bytes, alignment: 1 bytes +print-type-size field `.value`: 1024 bytes +print-type-size type: `std::mem::MaybeUninit<[u8; 1024]>`: 1024 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1024 bytes +print-type-size field `.uninit`: 0 bytes +print-type-size field `.value`: 1024 bytes print-type-size type: `std::mem::ManuallyDrop`: 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 diff --git a/tests/ui/async-await/future-sizes/future-as-arg.rs b/tests/ui/async-await/future-sizes/future-as-arg.rs index 317c17b9b3e59..70ed03c94df3c 100644 --- a/tests/ui/async-await/future-sizes/future-as-arg.rs +++ b/tests/ui/async-await/future-sizes/future-as-arg.rs @@ -8,9 +8,10 @@ async fn use_future(fut: impl std::future::Future) { } fn main() { - let actual = std::mem::size_of_val( - &use_future(use_future(use_future(use_future(use_future(test([0; 16]))))))); + let actual = std::mem::size_of_val(&use_future(use_future(use_future(use_future( + use_future(test([0; 16])), + ))))); // Not using an exact number in case it slightly changes over different commits let expected = 550; - assert!(actual > expected, "expected: >{expected}, actual: {actual}"); + assert!(actual < expected, "expected: >{expected}, actual: {actual}"); } diff --git a/tests/ui/async-await/future-sizes/large-arg.stdout b/tests/ui/async-await/future-sizes/large-arg.stdout index 67168a3d6ef74..4a68e5d9b7f0f 100644 --- a/tests/ui/async-await/future-sizes/large-arg.stdout +++ b/tests/ui/async-await/future-sizes/large-arg.stdout @@ -1,44 +1,38 @@ -print-type-size type: `{async fn body of test()}`: 3076 bytes, alignment: 1 bytes +print-type-size type: `{async fn body of test()}`: 1028 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes -print-type-size variant `Suspend0`: 3075 bytes -print-type-size local `.__awaitee`: 3075 bytes, type: {async fn body of a<[u8; 1024]>()} +print-type-size variant `Suspend0`: 1027 bytes +print-type-size local `.__awaitee`: 1027 bytes, type: {async fn body of a<[u8; 1024]>()} print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes -print-type-size type: `std::mem::ManuallyDrop<{async fn body of a<[u8; 1024]>()}>`: 3075 bytes, alignment: 1 bytes -print-type-size field `.value`: 3075 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body of a<[u8; 1024]>()}>`: 3075 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 3075 bytes +print-type-size type: `std::mem::ManuallyDrop<{async fn body of a<[u8; 1024]>()}>`: 1027 bytes, alignment: 1 bytes +print-type-size field `.value`: 1027 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body of a<[u8; 1024]>()}>`: 1027 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1027 bytes print-type-size field `.uninit`: 0 bytes -print-type-size field `.value`: 3075 bytes -print-type-size type: `{async fn body of a<[u8; 1024]>()}`: 3075 bytes, alignment: 1 bytes +print-type-size field `.value`: 1027 bytes +print-type-size type: `{async fn body of a<[u8; 1024]>()}`: 1027 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size variant `Suspend0`: 3074 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size local `.__awaitee`: 2050 bytes, type: {async fn body of b<[u8; 1024]>()} -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::mem::ManuallyDrop<{async fn body of b<[u8; 1024]>()}>`: 2050 bytes, alignment: 1 bytes -print-type-size field `.value`: 2050 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body of b<[u8; 1024]>()}>`: 2050 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 2050 bytes +print-type-size local `.t`: 1024 bytes +print-type-size variant `Suspend0`: 1026 bytes +print-type-size local `.__awaitee`: 1026 bytes, type: {async fn body of b<[u8; 1024]>()} +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<{async fn body of b<[u8; 1024]>()}>`: 1026 bytes, alignment: 1 bytes +print-type-size field `.value`: 1026 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body of b<[u8; 1024]>()}>`: 1026 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1026 bytes print-type-size field `.uninit`: 0 bytes -print-type-size field `.value`: 2050 bytes -print-type-size type: `{async fn body of b<[u8; 1024]>()}`: 2050 bytes, alignment: 1 bytes +print-type-size field `.value`: 1026 bytes +print-type-size type: `{async fn body of b<[u8; 1024]>()}`: 1026 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size variant `Suspend0`: 2049 bytes -print-type-size upvar `.t`: 1024 bytes +print-type-size local `.t`: 1024 bytes +print-type-size variant `Suspend0`: 1025 bytes print-type-size local `.__awaitee`: 1025 bytes, type: {async fn body of c<[u8; 1024]>()} -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 variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body of c<[u8; 1024]>()}>`: 1025 bytes, alignment: 1 bytes print-type-size field `.value`: 1025 bytes print-type-size type: `std::mem::MaybeUninit<{async fn body of c<[u8; 1024]>()}>`: 1025 bytes, alignment: 1 bytes @@ -53,8 +47,12 @@ print-type-size variant `Pending`: 0 bytes print-type-size type: `{async fn body of c<[u8; 1024]>()}`: 1025 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -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 local `.t`: 1024 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<[u8; 1024]>`: 1024 bytes, alignment: 1 bytes +print-type-size field `.value`: 1024 bytes +print-type-size type: `std::mem::MaybeUninit<[u8; 1024]>`: 1024 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1024 bytes +print-type-size field `.uninit`: 0 bytes +print-type-size field `.value`: 1024 bytes diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr index 8126c6e13942e..b7feae0333e90 100644 --- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr @@ -3,6 +3,9 @@ error[E0733]: recursion in an async fn requires boxing | LL | async fn second(self) { | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | self.first().await.second().await; + | --------------------------------- recursive call here | = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future diff --git a/tests/ui/async-await/issue-70818.rs b/tests/ui/async-await/issue-70818.rs index 36295a84e7ad7..837d532e067d1 100644 --- a/tests/ui/async-await/issue-70818.rs +++ b/tests/ui/async-await/issue-70818.rs @@ -3,6 +3,7 @@ use std::future::Future; fn foo(ty: T, ty1: U) -> impl Future + Send { //~^ Error future cannot be sent between threads safely + //~| Error future cannot be sent between threads safely async { (ty, ty1) } } diff --git a/tests/ui/async-await/issue-70818.stderr b/tests/ui/async-await/issue-70818.stderr index 317c04d2c7478..a51b0375098b0 100644 --- a/tests/ui/async-await/issue-70818.stderr +++ b/tests/ui/async-await/issue-70818.stderr @@ -5,7 +5,7 @@ LL | fn foo(ty: T, ty1: U) -> impl Future + Send { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send` | note: captured value is not `Send` - --> $DIR/issue-70818.rs:6:18 + --> $DIR/issue-70818.rs:7:18 | LL | async { (ty, ty1) } | ^^^ has type `U` which is not `Send` @@ -14,5 +14,22 @@ help: consider restricting type parameter `U` LL | fn foo(ty: T, ty1: U) -> impl Future + Send { | +++++++++++++++++++ -error: aborting due to 1 previous error +error: future cannot be sent between threads safely + --> $DIR/issue-70818.rs:4:38 + | +LL | fn foo(ty: T, ty1: U) -> impl Future + Send { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send` + | +note: captured value is not `Send` + --> $DIR/issue-70818.rs:7:18 + | +LL | async { (ty, ty1) } + | ^^^ has type `U` which is not `Send` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider restricting type parameter `U` + | +LL | fn foo(ty: T, ty1: U) -> impl Future + Send { + | +++++++++++++++++++ + +error: aborting due to 2 previous errors diff --git a/tests/ui/async-await/issue-86507.rs b/tests/ui/async-await/issue-86507.rs index 484122a1ddcfd..e959f2c1166f4 100644 --- a/tests/ui/async-await/issue-86507.rs +++ b/tests/ui/async-await/issue-86507.rs @@ -15,6 +15,7 @@ impl Foo for () { -> Pin + Send + 'async_trait>> where 'me:'async_trait { Box::pin( //~ ERROR future cannot be sent between threads safely + //~^ ERROR future cannot be sent between threads safely async move { let x = x; } diff --git a/tests/ui/async-await/issue-86507.stderr b/tests/ui/async-await/issue-86507.stderr index 0398e57ef780f..c4ffa1f6be752 100644 --- a/tests/ui/async-await/issue-86507.stderr +++ b/tests/ui/async-await/issue-86507.stderr @@ -2,6 +2,7 @@ error: future cannot be sent between threads safely --> $DIR/issue-86507.rs:17:13 | LL | / Box::pin( +LL | | LL | | async move { LL | | let x = x; LL | | } @@ -9,15 +10,37 @@ LL | | ) | |_____________^ future created by async block is not `Send` | note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> $DIR/issue-86507.rs:19:29 + --> $DIR/issue-86507.rs:20:29 | LL | let x = x; | ^ has type `&T` which is not `Send`, because `T` is not `Sync` - = note: required for the cast from `Pin>` to `Pin + Send + 'async_trait)>>` + = note: required for the cast from `Pin>` to `Pin + Send + 'async_trait)>>` help: consider further restricting this bound | LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T) | +++++++++++++++++++ -error: aborting due to 1 previous error +error: future cannot be sent between threads safely + --> $DIR/issue-86507.rs:17:13 + | +LL | / Box::pin( +LL | | +LL | | async move { +LL | | let x = x; +LL | | } +LL | | ) + | |_____________^ future created by async block is not `Send` + | +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/issue-86507.rs:20:29 + | +LL | let x = x; + | ^ has type `&T` which is not `Send`, because `T` is not `Sync` + = note: required for the cast from `Pin>` to `Pin + Send>>` +help: consider further restricting this bound + | +LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T) + | +++++++++++++++++++ + +error: aborting due to 2 previous errors diff --git a/tests/ui/coroutine/clone-impl.stderr b/tests/ui/coroutine/clone-impl.stderr index 1d4804501d8bc..1109257cfb333 100644 --- a/tests/ui/coroutine/clone-impl.stderr +++ b/tests/ui/coroutine/clone-impl.stderr @@ -7,11 +7,14 @@ LL | let gen_clone_0 = move || { LL | check_copy(&gen_clone_0); | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:36:23: 36:30}`, the trait `Copy` is not implemented for `Vec`, which is required by `{coroutine@$DIR/clone-impl.rs:36:23: 36:30}: Copy` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:40:14 +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:38:9 | -LL | drop(clonable_0); - | ^^^^^^^^^^ has type `Vec` which does not implement `Copy` +LL | let clonable_0: Vec = Vec::new(); + | ---------- has type `Vec` which does not implement `Copy` +... +LL | yield; + | ^^^^^ yield occurs here, with `clonable_0` maybe used later note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:72:18 | @@ -49,11 +52,14 @@ LL | let gen_clone_1 = move || { LL | check_copy(&gen_clone_1); | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:46:23: 46:30}`, the trait `Copy` is not implemented for `Vec`, which is required by `{coroutine@$DIR/clone-impl.rs:46:23: 46:30}: Copy` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:56:14 +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:52:9 | -LL | drop(clonable_1); - | ^^^^^^^^^^ has type `Vec` which does not implement `Copy` +LL | let clonable_1: Vec = Vec::new(); + | ---------- has type `Vec` which does not implement `Copy` +... +LL | yield; + | ^^^^^ yield occurs here, with `clonable_1` maybe used later note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:72:18 | @@ -92,11 +98,14 @@ LL | let gen_non_clone = move || { LL | check_copy(&gen_non_clone); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}`, the trait `Copy` is not implemented for `NonClone`, which is required by `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}: Copy` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:64:14 +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:63:9 | -LL | drop(non_clonable); - | ^^^^^^^^^^^^ has type `NonClone` which does not implement `Copy` +LL | let non_clonable: NonClone = NonClone; + | ------------ has type `NonClone` which does not implement `Copy` +... +LL | yield; + | ^^^^^ yield occurs here, with `non_clonable` maybe used later note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:72:18 | @@ -117,11 +126,14 @@ LL | let gen_non_clone = move || { LL | check_clone(&gen_non_clone); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}`, the trait `Clone` is not implemented for `NonClone`, which is required by `{coroutine@$DIR/clone-impl.rs:62:25: 62:32}: Clone` | -note: captured value does not implement `Clone` - --> $DIR/clone-impl.rs:64:14 +note: coroutine does not implement `Clone` as this value is used across a yield + --> $DIR/clone-impl.rs:63:9 | -LL | drop(non_clonable); - | ^^^^^^^^^^^^ has type `NonClone` which does not implement `Clone` +LL | let non_clonable: NonClone = NonClone; + | ------------ has type `NonClone` which does not implement `Clone` +... +LL | yield; + | ^^^^^ yield occurs here, with `non_clonable` maybe used later note: required by a bound in `check_clone` --> $DIR/clone-impl.rs:73:19 | diff --git a/tests/ui/coroutine/ref-upvar-not-send.rs b/tests/ui/coroutine/ref-upvar-not-send.rs index 487fdeea2dae9..5489e6d8b8e13 100644 --- a/tests/ui/coroutine/ref-upvar-not-send.rs +++ b/tests/ui/coroutine/ref-upvar-not-send.rs @@ -8,6 +8,10 @@ fn assert_send(_: T) {} //~| NOTE required by this bound in `assert_send` //~| NOTE required by a bound in `assert_send` //~| NOTE required by this bound in `assert_send` +//~| NOTE required by a bound in `assert_send` +//~| NOTE required by this bound in `assert_send` +//~| NOTE required by a bound in `assert_send` +//~| NOTE required by this bound in `assert_send` fn main() { let x: &*mut () = &std::ptr::null_mut(); @@ -15,17 +19,25 @@ fn main() { assert_send(move || { //~^ ERROR coroutine cannot be sent between threads safely //~| NOTE coroutine is not `Send` + //~| ERROR coroutine cannot be sent between threads safely + //~| NOTE coroutine is not `Send` yield; let _x = x; + //~^ NOTE captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + //~| NOTE has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` + //~| NOTE captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + //~| NOTE has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` }); - //~^^ NOTE captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - //~| NOTE has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` assert_send(move || { //~^ ERROR coroutine cannot be sent between threads safely //~| NOTE coroutine is not `Send` + //~| ERROR coroutine cannot be sent between threads safely + //~| NOTE coroutine is not `Send` yield; let _y = y; + //~^ captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + //~| has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` + //~| captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + //~| has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` }); - //~^^ NOTE captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` - //~| NOTE has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` } diff --git a/tests/ui/coroutine/ref-upvar-not-send.stderr b/tests/ui/coroutine/ref-upvar-not-send.stderr index 0f91bcf40533f..e50db54e1aa14 100644 --- a/tests/ui/coroutine/ref-upvar-not-send.stderr +++ b/tests/ui/coroutine/ref-upvar-not-send.stderr @@ -1,18 +1,19 @@ error: coroutine cannot be sent between threads safely - --> $DIR/ref-upvar-not-send.rs:15:17 + --> $DIR/ref-upvar-not-send.rs:19:17 | LL | assert_send(move || { | _________________^ LL | | LL | | -LL | | yield; -LL | | let _x = x; +LL | | +... | +LL | | LL | | }); | |_____^ coroutine is not `Send` | - = help: the trait `Sync` is not implemented for `*mut ()`, which is required by `{coroutine@$DIR/ref-upvar-not-send.rs:15:17: 15:24}: Send` + = help: the trait `Sync` is not implemented for `*mut ()`, which is required by `{coroutine@$DIR/ref-upvar-not-send.rs:19:17: 19:24}: Send` note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> $DIR/ref-upvar-not-send.rs:19:18 + --> $DIR/ref-upvar-not-send.rs:25:18 | LL | let _x = x; | ^ has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` @@ -23,20 +24,69 @@ LL | fn assert_send(_: T) {} | ^^^^ required by this bound in `assert_send` error: coroutine cannot be sent between threads safely - --> $DIR/ref-upvar-not-send.rs:23:17 + --> $DIR/ref-upvar-not-send.rs:31:17 | LL | assert_send(move || { | _________________^ LL | | LL | | -LL | | yield; -LL | | let _y = y; +LL | | +... | +LL | | LL | | }); | |_____^ coroutine is not `Send` | - = help: within `{coroutine@$DIR/ref-upvar-not-send.rs:23:17: 23:24}`, the trait `Send` is not implemented for `*mut ()`, which is required by `{coroutine@$DIR/ref-upvar-not-send.rs:23:17: 23:24}: Send` + = help: within `{coroutine@$DIR/ref-upvar-not-send.rs:31:17: 31:24}`, the trait `Send` is not implemented for `*mut ()`, which is required by `{coroutine@$DIR/ref-upvar-not-send.rs:31:17: 31:24}: Send` +note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + --> $DIR/ref-upvar-not-send.rs:37:18 + | +LL | let _y = y; + | ^ has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` +note: required by a bound in `assert_send` + --> $DIR/ref-upvar-not-send.rs:6:19 + | +LL | fn assert_send(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: coroutine cannot be sent between threads safely + --> $DIR/ref-upvar-not-send.rs:19:5 + | +LL | / assert_send(move || { +LL | | +LL | | +LL | | +... | +LL | | +LL | | }); + | |______^ coroutine is not `Send` + | + = help: the trait `Sync` is not implemented for `*mut ()`, which is required by `{coroutine@$DIR/ref-upvar-not-send.rs:19:17: 19:24}: Send` +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/ref-upvar-not-send.rs:25:18 + | +LL | let _x = x; + | ^ has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` +note: required by a bound in `assert_send` + --> $DIR/ref-upvar-not-send.rs:6:19 + | +LL | fn assert_send(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: coroutine cannot be sent between threads safely + --> $DIR/ref-upvar-not-send.rs:31:5 + | +LL | / assert_send(move || { +LL | | +LL | | +LL | | +... | +LL | | +LL | | }); + | |______^ coroutine is not `Send` + | + = help: within `{coroutine@$DIR/ref-upvar-not-send.rs:31:17: 31:24}`, the trait `Send` is not implemented for `*mut ()`, which is required by `{coroutine@$DIR/ref-upvar-not-send.rs:31:17: 31:24}: Send` note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` - --> $DIR/ref-upvar-not-send.rs:27:18 + --> $DIR/ref-upvar-not-send.rs:37:18 | LL | let _y = y; | ^ has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` @@ -46,5 +96,5 @@ note: required by a bound in `assert_send` LL | fn assert_send(_: T) {} | ^^^^ required by this bound in `assert_send` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/coroutine/unsized-capture-across-yield.rs b/tests/ui/coroutine/unsized-capture-across-yield.rs index ef9cbc1d677da..17afd88c2a282 100644 --- a/tests/ui/coroutine/unsized-capture-across-yield.rs +++ b/tests/ui/coroutine/unsized-capture-across-yield.rs @@ -7,6 +7,7 @@ use std::ops::Coroutine; fn capture() -> impl Coroutine { let b: [u8] = *(Box::new([]) as Box<[u8]>); + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time move || { println!("{:?}", &b); //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time diff --git a/tests/ui/coroutine/unsized-capture-across-yield.stderr b/tests/ui/coroutine/unsized-capture-across-yield.stderr index 436f0901a9776..59d9b39c71187 100644 --- a/tests/ui/coroutine/unsized-capture-across-yield.stderr +++ b/tests/ui/coroutine/unsized-capture-across-yield.stderr @@ -8,7 +8,7 @@ LL | #![feature(unsized_locals)] = note: `#[warn(incomplete_features)]` on by default error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/unsized-capture-across-yield.rs:11:27 + --> $DIR/unsized-capture-across-yield.rs:12:27 | LL | move || { | -- this closure captures all values by move @@ -18,6 +18,15 @@ LL | println!("{:?}", &b); = help: the trait `Sized` is not implemented for `[u8]` = note: all values captured by value by a closure must have a statically known size -error: aborting due to 1 previous error; 1 warning emitted +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-capture-across-yield.rs:9:9 + | +LL | let b: [u8] = *(Box::new([]) as Box<[u8]>); + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all values live across `yield` must have a statically known size + +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index d5b8c531fd6e0..d31e83a1a4fc3 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -92,7 +92,9 @@ error[E0720]: cannot resolve opaque type | LL | fn coroutine_capture() -> impl Sized { | ^^^^^^^^^^ recursive opaque type -... +LL | +LL | let x = coroutine_capture(); + | - coroutine captures itself here LL | / move || { LL | | yield; LL | | x; diff --git a/tests/ui/print_type_sizes/async.stdout b/tests/ui/print_type_sizes/async.stdout index 83a6962e4cd13..be79872383e1b 100644 --- a/tests/ui/print_type_sizes/async.stdout +++ b/tests/ui/print_type_sizes/async.stdout @@ -1,15 +1,14 @@ print-type-size type: `{async fn body of test()}`: 16386 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes -print-type-size variant `Unresumed`: 8192 bytes -print-type-size upvar `.arg`: 8192 bytes +print-type-size variant `Unresumed`: 16385 bytes +print-type-size padding: 8193 bytes +print-type-size local `.arg`: 8192 bytes, alignment: 1 bytes print-type-size variant `Suspend0`: 16385 bytes -print-type-size upvar `.arg`: 8192 bytes print-type-size local `.arg`: 8192 bytes print-type-size local `.__awaitee`: 1 bytes, type: {async fn body of wait()} -print-type-size variant `Returned`: 8192 bytes -print-type-size upvar `.arg`: 8192 bytes -print-type-size variant `Panicked`: 8192 bytes -print-type-size upvar `.arg`: 8192 bytes +print-type-size local `.arg`: 8192 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes print-type-size field `.value`: 8192 bytes print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes diff --git a/tests/ui/print_type_sizes/coroutine.stdout b/tests/ui/print_type_sizes/coroutine.stdout index 5d51339558caf..a186e7e453f61 100644 --- a/tests/ui/print_type_sizes/coroutine.stdout +++ b/tests/ui/print_type_sizes/coroutine.stdout @@ -1,10 +1,14 @@ print-type-size type: `{coroutine@$DIR/coroutine.rs:10:5: 10:14}`: 8193 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 8192 bytes -print-type-size upvar `.array`: 8192 bytes +print-type-size local `.array`: 8192 bytes print-type-size variant `Suspend0`: 8192 bytes -print-type-size upvar `.array`: 8192 bytes -print-type-size variant `Returned`: 8192 bytes -print-type-size upvar `.array`: 8192 bytes -print-type-size variant `Panicked`: 8192 bytes -print-type-size upvar `.array`: 8192 bytes +print-type-size local `.array`: 8192 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes +print-type-size field `.value`: 8192 bytes +print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 8192 bytes +print-type-size field `.uninit`: 0 bytes +print-type-size field `.value`: 8192 bytes