diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 2d269e320b64d..320d68190cc27 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1267,10 +1267,12 @@ impl<'tcx> InferCtxt<'tcx> { // to handle them without proper canonicalization. This means we may cause cycle // errors and fail to reveal opaques while inside of bodies. We should rename this // function and require explicit comments on all use-sites in the future. - ty::TypingMode::Analysis { defining_opaque_types_and_generators: _ } - | ty::TypingMode::Borrowck { defining_opaque_types: _ } => { + ty::TypingMode::Analysis { defining_opaque_types_and_generators: _ } => { TypingMode::non_body_analysis() } + ty::TypingMode::Borrowck { defining_opaque_types: _ } => { + TypingMode::Borrowck { defining_opaque_types: ty::List::empty() } + } mode @ (ty::TypingMode::Coherence | ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => mode, diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 38cfdcdc22d33..1fc144518402b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -344,9 +344,12 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( let args = args.as_coroutine(); // While we conservatively assume that all coroutines require drop - // to avoid query cycles during MIR building, we can check the actual - // witness during borrowck to avoid unnecessary liveness constraints. - if args.witness().needs_drop(tcx, tcx.erase_regions(typing_env)) { + // to avoid query cycles during MIR building, we can be more precise + // here by re-checking in a `TypingMode::Borrowck` environment. This + // will recurse into the coroutine witness (which we can now access + // without cycles). + let needs_drop = ty.needs_drop(tcx, tcx.erase_regions(typing_env)); + if needs_drop { constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from)); constraints.outlives.push(args.resume_ty().into()); } diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 5eddad39e2be2..cf7873975929c 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -23,7 +23,6 @@ fn dropck_outlives<'tcx>( canonical_goal: CanonicalDropckOutlivesGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution> { debug!("dropck_outlives(goal={:#?})", canonical_goal); - tcx.infer_ctxt().enter_canonical_trait_query(&canonical_goal, |ocx, goal| { compute_dropck_outlives_inner(ocx, goal, DUMMY_SP) }) diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index c3b04c20f4b67..d4eb0ab9c2e5a 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -101,9 +101,6 @@ fn has_significant_drop_raw<'tcx>( struct NeedsDropTypes<'tcx, F> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - /// Whether to reveal coroutine witnesses, this is set - /// to `false` unless we compute `needs_drop` for a coroutine witness. - reveal_coroutine_witnesses: bool, query_ty: Ty<'tcx>, seen_tys: FxHashSet>, /// A stack of types left to process, and the recursion depth when we @@ -131,7 +128,6 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> { Self { tcx, typing_env, - reveal_coroutine_witnesses: exhaustive, seen_tys, query_ty: ty, unchecked_tys: vec![(ty, 0)], @@ -196,15 +192,28 @@ where // need to be dropped, and only require the captured types to be live // if they do. ty::Coroutine(_, args) => { - if self.reveal_coroutine_witnesses { - queue_type(self, args.as_coroutine().witness()); - } else { - return Some(self.always_drop_component(ty)); + for arg in args.as_coroutine().upvar_tys() { + queue_type(self, arg); + } + queue_type(self, args.as_coroutine().resume_ty()); + match self.typing_env.typing_mode { + ty::TypingMode::Coherence => { + unreachable!("coherence should not be considering drops") + } + ty::TypingMode::Analysis { .. } => { + return Some( + self.always_drop_component(args.as_coroutine().witness()), + ); + } + ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + queue_type(self, args.as_coroutine().witness()); + } } } ty::CoroutineWitness(def_id, args) => { if let Some(witness) = tcx.mir_coroutine_witnesses(def_id) { - self.reveal_coroutine_witnesses = true; for field_ty in &witness.field_tys { queue_type( self, diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index e86a2305e2334..f3fc10c6e78dd 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -117,11 +117,11 @@ impl TypingMode { } pub fn borrowck(cx: I, body_def_id: I::LocalDefId) -> TypingMode { - let defining_opaque_types = cx.opaque_types_defined_by(body_def_id); - if defining_opaque_types.is_empty() { + // N.B. we can only use an analysis env if there are no coroutines defined. + if cx.opaque_types_and_coroutines_defined_by(body_def_id).is_empty() { TypingMode::non_body_analysis() } else { - TypingMode::Borrowck { defining_opaque_types } + TypingMode::Borrowck { defining_opaque_types: cx.opaque_types_defined_by(body_def_id) } } } diff --git a/tests/ui/async-await/drop-live-upvar.rs b/tests/ui/async-await/drop-live-upvar.rs new file mode 100644 index 0000000000000..8e881f729b910 --- /dev/null +++ b/tests/ui/async-await/drop-live-upvar.rs @@ -0,0 +1,23 @@ +//@ edition: 2018 +// Regression test for . + +struct NeedsDrop<'a>(&'a Vec); + +async fn await_point() {} + +impl Drop for NeedsDrop<'_> { + fn drop(&mut self) {} +} + +fn foo() { + let v = vec![1, 2, 3]; + let x = NeedsDrop(&v); + let c = async { + std::future::ready(()).await; + drop(x); + }; + drop(v); + //~^ ERROR cannot move out of `v` because it is borrowed +} + +fn main() {} diff --git a/tests/ui/async-await/drop-live-upvar.stderr b/tests/ui/async-await/drop-live-upvar.stderr new file mode 100644 index 0000000000000..f804484536baf --- /dev/null +++ b/tests/ui/async-await/drop-live-upvar.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `v` because it is borrowed + --> $DIR/drop-live-upvar.rs:19:10 + | +LL | let v = vec![1, 2, 3]; + | - binding `v` declared here +LL | let x = NeedsDrop(&v); + | -- borrow of `v` occurs here +... +LL | drop(v); + | ^ move out of `v` occurs here +LL | +LL | } + | - borrow might be used here, when `c` is dropped and runs the destructor for coroutine + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = NeedsDrop(&v.clone()); + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`.