diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index c45a4a410f964..f1d246503cab5 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1510,8 +1510,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 cf28e62177fb4..215c299216ea5 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -802,14 +802,11 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }), }; } - 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) => { @@ -1822,11 +1819,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::Array(ty) => Ok(ty), @@ -2443,7 +2439,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 0afd6d0e670b3..7be8f40039e5d 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -780,6 +780,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 76c9ac6614a30..1177015474a8b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -29,6 +29,7 @@ use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::List; use rustc_middle::ty::{ self, AdtKind, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt, Visibility, }; @@ -1066,7 +1067,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, List::empty()), ty::Closure(def_id, args) => (def_id, args.as_closure().upvar_tys()), _ => { bug!( 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 4792b0798dfb8..ff4345df85403 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 @@ -685,7 +685,6 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( let coroutine_layout = cx.tcx.optimized_mir(coroutine_def_id).coroutine_layout().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(); @@ -720,7 +719,6 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( coroutine_type_and_layout, coroutine_type_di_node, coroutine_layout, - common_upvar_names, ); let span = coroutine_layout.variant_source_info[variant_index].span; 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 7f671d1d06129..024cc3a1748f5 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -3,7 +3,6 @@ use rustc_codegen_ssa::debuginfo::{ wants_c_like_enum_debuginfo, }; use rustc_hir::def::CtorKind; -use rustc_index::IndexSlice; use rustc_middle::{ bug, mir::CoroutineLayout, @@ -13,7 +12,6 @@ use rustc_middle::{ AdtDef, CoroutineArgs, Ty, VariantDef, }, }; -use rustc_span::Symbol; use rustc_target::abi::{ FieldIdx, HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants, }; @@ -324,7 +322,6 @@ 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, ) -> &'ll DIType { let variant_name = CoroutineArgs::variant_name(variant_index); let unique_type_id = UniqueTypeId::for_enum_variant_struct_type( @@ -335,11 +332,6 @@ 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() { - ty::Coroutine(_, args) => args.as_coroutine(), - _ => unreachable!(), - }; - type_map::build_type_with_children( cx, type_map::stub( @@ -353,7 +345,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)]; @@ -375,28 +367,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_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 3dbe820b8ff9b..8d2a7313121ac 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -170,9 +170,6 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( ) }; - let common_upvar_names = - cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); - // Build variant struct types let variant_struct_type_di_nodes: SmallVec<_> = variants .indices() @@ -200,7 +197,6 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( coroutine_type_and_layout, coroutine_type_di_node, coroutine_layout, - common_upvar_names, ), source_info, } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 266505d3f2691..06f36e1d9d1da 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 d48329b6c6946..1aaf4717e19bd 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -295,6 +295,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 21bdb66a27631..107182a9b62ba 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -695,14 +695,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/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 5597609c7d796..055384ee384fc 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 25473f52c039e..0c41138e16bf0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -919,7 +919,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 b089f4a9e78b9..93961beae2115 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -542,13 +542,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 595c2ff5bf7fa..69c81f4e7e56e 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 { .. } @@ -304,12 +351,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 @@ -317,7 +408,6 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { TerminatorKind::Yield { .. } | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Assert { .. } - | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::CoroutineDrop @@ -338,7 +428,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) + } + }); } } @@ -346,7 +449,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); } } @@ -354,6 +461,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> @@ -361,11 +469,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 cbbf3548c07ef..cfb5b4937463c 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 bde879f6067c5..6507aaa066b9a 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -61,6 +61,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::*; @@ -77,7 +78,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; @@ -246,9 +247,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); @@ -480,15 +485,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); } } @@ -565,7 +577,7 @@ 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_ref( tcx, @@ -574,14 +586,14 @@ fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Bo ); // 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); @@ -589,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); @@ -758,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() @@ -796,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. @@ -819,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 @@ -859,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(), ); @@ -927,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); @@ -941,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); @@ -956,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. @@ -1029,9 +1062,11 @@ impl StorageConflictVisitor<'_, '_, '_> { } } +#[instrument(level = "debug", skip(liveness, body))] fn compute_layout<'tcx>( liveness: LivenessInfo, body: &Body<'tcx>, + upvars: IndexVec>, ) -> ( FxHashMap, VariantIdx, FieldIdx)>, CoroutineLayout<'tcx>, @@ -1044,37 +1079,52 @@ 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(); 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: body.local_decls[ty::CAPTURE_STRUCT_LOCAL].source_info, + ignore_for_traits: false, + }; + tys.push(decl); + } } // Leave empty variants for the UNRESUMED, RETURNED, and POISONED states. @@ -1086,15 +1136,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(); @@ -1105,11 +1168,25 @@ 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); @@ -1221,6 +1298,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>( @@ -1249,6 +1341,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 { @@ -1279,7 +1372,7 @@ 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(())); + dump_mir(tcx, false, "coroutine_drop", &0 as _, &body, |_, _| Ok(())); body.source.instance = drop_instance; body @@ -1465,7 +1558,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 { @@ -1530,7 +1623,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 { @@ -1570,21 +1663,27 @@ 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 (upvar_tys, movable) = match *coroutine_ty.kind() { + ty::Coroutine(def_id, args) => ( + 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 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); check_suspend_tys(tcx, &coroutine_layout, body); @@ -1602,14 +1701,18 @@ 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() { + let (discr_ty, upvar_tys, movable) = match *coroutine_ty.kind() { ty::Coroutine(_, args) => { let args = args.as_coroutine(); - (args.discr_ty(tcx), coroutine_kind.movability() == hir::Movability::Movable) + ( + args.discr_ty(tcx), + args.upvar_tys(), + matches!(coroutine_kind.movability(), hir::Movability::Movable), + ) } _ => { tcx.dcx().span_delayed_bug( @@ -1619,6 +1722,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { return; } }; + let upvars: IndexVec = upvar_tys.iter().collect(); let new_ret_ty = match coroutine_kind { CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => { @@ -1682,11 +1786,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 { @@ -1701,7 +1806,7 @@ 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); let can_return = can_return(tcx, body, tcx.param_env(body.source.def_id())); @@ -1721,6 +1826,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); @@ -1749,14 +1855,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/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 0ac4ab61d409c..de80d90dd2a3e 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_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 2fc4bfd4aa31c..be294f006c89e 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -664,12 +664,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 @@ -689,6 +691,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 @@ -740,7 +743,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; @@ -756,14 +759,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, @@ -818,6 +815,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] { @@ -956,11 +954,10 @@ fn record_layout_for_printing<'tcx>(cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: T record(adt_kind.into(), adt_packed, opt_discr_size, variant_infos); } - ty::Coroutine(def_id, args) => { + ty::Coroutine(def_id, _args) => { debug!("print-type-size t: `{:?}` record coroutine", layout.ty); // Coroutines always have a begin/poisoned/end state with additional suspend points - let (variant_infos, opt_discr_size) = - variant_info_for_coroutine(cx, layout, def_id, args); + let (variant_infos, opt_discr_size) = variant_info_for_coroutine(cx, layout, def_id); record(DataTypeKind::Coroutine, false, opt_discr_size, variant_infos); } @@ -1050,37 +1047,12 @@ fn variant_info_for_coroutine<'tcx>( cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: TyAndLayout<'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.optimized_mir(def_id).coroutine_layout().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(), - } - }) - .collect(); let mut variant_infos: Vec<_> = coroutine .variant_fields @@ -1107,14 +1079,8 @@ fn variant_info_for_coroutine<'tcx>( align: field_layout.align.abi.bytes(), } }) - .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/tests/ui/async-await/async-await-let-else.stderr b/tests/ui/async-await/async-await-let-else.stderr index b360aab6b595f..b2ca7e8dd7747 100644 --- a/tests/ui/async-await/async-await-let-else.stderr +++ b/tests/ui/async-await/async-await-let-else.stderr @@ -38,7 +38,7 @@ LL | async fn bar2(_: T) -> ! { LL | | panic!() LL | | } | |_^ - = note: required because it captures the following types: `impl Future` + = note: required because it captures the following types: `impl Future`, `Option` note: required because it's used within this `async` fn body --> $DIR/async-await-let-else.rs:18:32 | 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 47b39e5246dde..c4afcf070e250 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@$DIR/async-awaiting-fut.rs:21:21: 24:2}`: 3078 bytes, alignment: 1 bytes +print-type-size type: `{async fn body@$DIR/async-awaiting-fut.rs:21:21: 24:2}`: 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 +print-type-size variant `Suspend0`: 2052 bytes +print-type-size local `.__awaitee`: 2052 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@$DIR/async-awaiting-fut.rs:10:64: 19:2}>`: 3077 bytes, alignment: 1 bytes -print-type-size field `.value`: 3077 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/async-awaiting-fut.rs:10:64: 19:2}>`: 3077 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 3077 bytes +print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/async-awaiting-fut.rs:10:64: 19:2}>`: 2052 bytes, alignment: 1 bytes +print-type-size field `.value`: 2052 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/async-awaiting-fut.rs:10:64: 19:2}>`: 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@$DIR/async-awaiting-fut.rs:10:64: 19:2}`: 3077 bytes, alignment: 1 bytes +print-type-size field `.value`: 2052 bytes +print-type-size type: `{async fn body@$DIR/async-awaiting-fut.rs:10:64: 19:2}`: 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 local `.fut`: 1025 bytes, alignment: 1 bytes +print-type-size variant `Unresumed`: 2051 bytes +print-type-size padding: 1026 bytes +print-type-size local `..coroutine_field5`: 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 print-type-size local `.__awaitee`: 1 bytes -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 print-type-size local `.__awaitee`: 1025 bytes -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 print-type-size local `.__awaitee`: 1 bytes -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@$DIR/async-awaiting-fut.rs:8:35: 8:37}>`: 1025 bytes, alignment: 1 bytes print-type-size field `.value`: 1025 bytes print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/async-awaiting-fut.rs:8:35: 8:37}>`: 1025 bytes, alignment: 1 bytes @@ -45,11 +39,15 @@ print-type-size field `.value`: 1025 bytes print-type-size type: `{async fn body@$DIR/async-awaiting-fut.rs:8:35: 8:37}`: 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 `..coroutine_field0`: 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@$DIR/async-awaiting-fut.rs:6:17: 6:19}>`: 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 93c69b05254dc..69cbc7e5766f0 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 005460df626fc..c5dee9ea4a5ee 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@$DIR/large-arg.rs:6:21: 8:2}`: 3076 bytes, alignment: 1 bytes +print-type-size type: `{async fn body@$DIR/large-arg.rs:6:21: 8:2}`: 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 +print-type-size variant `Suspend0`: 1027 bytes +print-type-size local `.__awaitee`: 1027 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@$DIR/large-arg.rs:10:30: 12:2}>`: 3075 bytes, alignment: 1 bytes -print-type-size field `.value`: 3075 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/large-arg.rs:10:30: 12:2}>`: 3075 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 3075 bytes +print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/large-arg.rs:10:30: 12:2}>`: 1027 bytes, alignment: 1 bytes +print-type-size field `.value`: 1027 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/large-arg.rs:10:30: 12:2}>`: 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@$DIR/large-arg.rs:10:30: 12:2}`: 3075 bytes, alignment: 1 bytes +print-type-size field `.value`: 1027 bytes +print-type-size type: `{async fn body@$DIR/large-arg.rs:10:30: 12:2}`: 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 -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@$DIR/large-arg.rs:13:26: 15:2}>`: 2050 bytes, alignment: 1 bytes -print-type-size field `.value`: 2050 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/large-arg.rs:13:26: 15:2}>`: 2050 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 2050 bytes +print-type-size local `..coroutine_field1`: 1024 bytes +print-type-size variant `Suspend0`: 1026 bytes +print-type-size local `.__awaitee`: 1026 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@$DIR/large-arg.rs:13:26: 15:2}>`: 1026 bytes, alignment: 1 bytes +print-type-size field `.value`: 1026 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/large-arg.rs:13:26: 15:2}>`: 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@$DIR/large-arg.rs:13:26: 15:2}`: 2050 bytes, alignment: 1 bytes +print-type-size field `.value`: 1026 bytes +print-type-size type: `{async fn body@$DIR/large-arg.rs:13:26: 15:2}`: 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 `..coroutine_field1`: 1024 bytes +print-type-size variant `Suspend0`: 1025 bytes print-type-size local `.__awaitee`: 1025 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 variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/large-arg.rs:16:26: 18:2}>`: 1025 bytes, alignment: 1 bytes print-type-size field `.value`: 1025 bytes print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/large-arg.rs:16:26: 18:2}>`: 1025 bytes, alignment: 1 bytes @@ -53,8 +47,12 @@ print-type-size variant `Pending`: 0 bytes print-type-size type: `{async fn body@$DIR/large-arg.rs:16:26: 18:2}`: 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 `..coroutine_field0`: 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/issue-70935-complex-spans.stderr b/tests/ui/async-await/issue-70935-complex-spans.stderr index 14ef1cbb67c50..939ce0306508d 100644 --- a/tests/ui/async-await/issue-70935-complex-spans.stderr +++ b/tests/ui/async-await/issue-70935-complex-spans.stderr @@ -25,7 +25,7 @@ LL | async fn baz(_c: impl FnMut() -> T) where T: Future { | ___________________________________________________________________^ LL | | } | |_^ - = note: required because it captures the following types: `impl Future` + = note: required because it captures the following types: `impl Future`, `NotSync` note: required because it's used within this `async` block --> $DIR/issue-70935-complex-spans.rs:17:5 | diff --git a/tests/ui/coroutine/clone-impl.stderr b/tests/ui/coroutine/clone-impl.stderr index 82a6d0495c003..4fea360149cf0 100644 --- a/tests/ui/coroutine/clone-impl.stderr +++ b/tests/ui/coroutine/clone-impl.stderr @@ -7,11 +7,20 @@ 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` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:40:14 - | -LL | drop(clonable_0); - | ^^^^^^^^^^ has type `Vec` which does not implement `Copy` +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:36:23 + | +LL | let gen_clone_0 = move || { + | ^ + | | + | _______________________yield occurs here, with the value maybe used later + | | +LL | | let v = vec!['a']; +LL | | yield; +LL | | drop(v); +LL | | drop(clonable_0); +LL | | }; + | |_____- has type `Vec` which does not implement `Copy` note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:72:18 | @@ -49,11 +58,21 @@ 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` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:56:14 - | -LL | drop(clonable_1); - | ^^^^^^^^^^ has type `Vec` which does not implement `Copy` +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:46:23 + | +LL | let gen_clone_1 = move || { + | ^ + | | + | _______________________yield occurs here, with the value maybe used later + | | +LL | | let v = vec!['a']; +LL | | /* +LL | | let n = NonClone; +... | +LL | | drop(clonable_1); +LL | | }; + | |_____- has type `Vec` which does not implement `Copy` note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:72:18 | @@ -92,11 +111,18 @@ 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` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:64:14 - | -LL | drop(non_clonable); - | ^^^^^^^^^^^^ has type `NonClone` which does not implement `Copy` +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:62:25 + | +LL | let gen_non_clone = move || { + | ^ + | | + | _________________________yield occurs here, with the value maybe used later + | | +LL | | yield; +LL | | drop(non_clonable); +LL | | }; + | |_____- has type `NonClone` which does not implement `Copy` note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:72:18 | @@ -117,11 +143,18 @@ 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` | -note: captured value does not implement `Clone` - --> $DIR/clone-impl.rs:64:14 - | -LL | drop(non_clonable); - | ^^^^^^^^^^^^ has type `NonClone` which does not implement `Clone` +note: coroutine does not implement `Clone` as this value is used across a yield + --> $DIR/clone-impl.rs:62:25 + | +LL | let gen_non_clone = move || { + | ^ + | | + | _________________________yield occurs here, with the value maybe used later + | | +LL | | yield; +LL | | drop(non_clonable); +LL | | }; + | |_____- has type `NonClone` which does not implement `Clone` note: required by a bound in `check_clone` --> $DIR/clone-impl.rs:73:19 | 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..e4782522cf25f 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -98,7 +98,10 @@ LL | | yield; LL | | x; | | - coroutine captures itself here LL | | } - | |_____- returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:60:5: 60:12}` + | | - + | | | + | |_____returning here with type `{coroutine@$DIR/recursive-impl-trait-type-indirect.rs:60:5: 60:12}` + | coroutine captures itself here error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:66:35 diff --git a/tests/ui/print_type_sizes/async.stdout b/tests/ui/print_type_sizes/async.stdout index e1be98f85d847..6ee05c3f513ec 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@$DIR/async.rs:10:36: 13:2}`: 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 `..coroutine_field2`: 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 -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 `..coroutine_field2`: 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..4db9493c050cd 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 `..coroutine_field0`: 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 `..coroutine_field0`: 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 diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr index b62186103c7cb..ec33447c8fe79 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr @@ -6,13 +6,6 @@ LL | async move { recur(self).await; } | | | recursive call here | -note: which leads to this async fn - --> $DIR/indirect-recursion-issue-112047.rs:14:1 - | -LL | async fn recur(t: impl Recur) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | t.recur().await; - | --------------- ...leading to this recursive call = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future error: aborting due to 1 previous error