Skip to content

[WIP] Check coroutine upvars and resume ty in dtorck constraint, this time based off of TypingMode #144158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_traits/src/dropck_outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand Down
27 changes: 18 additions & 9 deletions compiler/rustc_ty_utils/src/needs_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ty<'tcx>>,
/// A stack of types left to process, and the recursion depth when we
Expand Down Expand Up @@ -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)],
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_type_ir/src/infer_ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ impl<I: Interner> TypingMode<I> {
}

pub fn borrowck(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> {
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) }
}
}

Expand Down
23 changes: 23 additions & 0 deletions tests/ui/async-await/drop-live-upvar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ edition: 2018
// Regression test for <https://github.com/rust-lang/rust/issues/144155>.

struct NeedsDrop<'a>(&'a Vec<i32>);

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() {}
22 changes: 22 additions & 0 deletions tests/ui/async-await/drop-live-upvar.stderr
Original file line number Diff line number Diff line change
@@ -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`.
Loading