dropck_outlives check generator witness needs_drop
This commit is contained in:
parent
f0df3d2dfb
commit
57253552de
@ -1107,8 +1107,10 @@ pub fn needs_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> boo
|
|||||||
// This doesn't depend on regions, so try to minimize distinct
|
// This doesn't depend on regions, so try to minimize distinct
|
||||||
// query keys used.
|
// query keys used.
|
||||||
// If normalization fails, we just use `query_ty`.
|
// If normalization fails, we just use `query_ty`.
|
||||||
let query_ty =
|
let param_env = tcx.erase_regions(param_env);
|
||||||
tcx.try_normalize_erasing_regions(param_env, query_ty).unwrap_or(query_ty);
|
let query_ty = tcx
|
||||||
|
.try_normalize_erasing_regions(param_env, query_ty)
|
||||||
|
.unwrap_or_else(|_| tcx.erase_regions(query_ty));
|
||||||
|
|
||||||
tcx.needs_drop_raw(param_env.and(query_ty))
|
tcx.needs_drop_raw(param_env.and(query_ty))
|
||||||
}
|
}
|
||||||
@ -1297,7 +1299,6 @@ pub fn needs_drop_components<'tcx>(
|
|||||||
| ty::FnDef(..)
|
| ty::FnDef(..)
|
||||||
| ty::FnPtr(_)
|
| ty::FnPtr(_)
|
||||||
| ty::Char
|
| ty::Char
|
||||||
| ty::CoroutineWitness(..)
|
|
||||||
| ty::RawPtr(_)
|
| ty::RawPtr(_)
|
||||||
| ty::Ref(..)
|
| ty::Ref(..)
|
||||||
| ty::Str => Ok(SmallVec::new()),
|
| ty::Str => Ok(SmallVec::new()),
|
||||||
@ -1337,7 +1338,8 @@ pub fn needs_drop_components<'tcx>(
|
|||||||
| ty::Placeholder(..)
|
| ty::Placeholder(..)
|
||||||
| ty::Infer(_)
|
| ty::Infer(_)
|
||||||
| ty::Closure(..)
|
| ty::Closure(..)
|
||||||
| ty::Coroutine(..) => Ok(smallvec![ty]),
|
| ty::Coroutine(..)
|
||||||
|
| ty::CoroutineWitness(..) => Ok(smallvec![ty]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ pub fn compute_dropck_outlives_inner<'tcx>(
|
|||||||
result.overflows.len(),
|
result.overflows.len(),
|
||||||
ty_stack.len()
|
ty_stack.len()
|
||||||
);
|
);
|
||||||
dtorck_constraint_for_ty_inner(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?;
|
dtorck_constraint_for_ty_inner(tcx, param_env, DUMMY_SP, depth, ty, &mut constraints)?;
|
||||||
|
|
||||||
// "outlives" represent types/regions that may be touched
|
// "outlives" represent types/regions that may be touched
|
||||||
// by a destructor.
|
// by a destructor.
|
||||||
@ -185,16 +185,15 @@ pub fn compute_dropck_outlives_inner<'tcx>(
|
|||||||
|
|
||||||
/// Returns a set of constraints that needs to be satisfied in
|
/// Returns a set of constraints that needs to be satisfied in
|
||||||
/// order for `ty` to be valid for destruction.
|
/// order for `ty` to be valid for destruction.
|
||||||
|
#[instrument(level = "debug", skip(tcx, param_env, span, constraints))]
|
||||||
pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
for_ty: Ty<'tcx>,
|
|
||||||
depth: usize,
|
depth: usize,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
constraints: &mut DropckConstraint<'tcx>,
|
constraints: &mut DropckConstraint<'tcx>,
|
||||||
) -> Result<(), NoSolution> {
|
) -> Result<(), NoSolution> {
|
||||||
debug!("dtorck_constraint_for_ty_inner({:?}, {:?}, {:?}, {:?})", span, for_ty, depth, ty);
|
|
||||||
|
|
||||||
if !tcx.recursion_limit().value_within_limit(depth) {
|
if !tcx.recursion_limit().value_within_limit(depth) {
|
||||||
constraints.overflows.push(ty);
|
constraints.overflows.push(ty);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -224,13 +223,13 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||||||
ty::Array(ety, _) | ty::Slice(ety) => {
|
ty::Array(ety, _) | ty::Slice(ety) => {
|
||||||
// single-element containers, behave like their element
|
// single-element containers, behave like their element
|
||||||
rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
||||||
dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, *ety, constraints)
|
dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, *ety, constraints)
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
||||||
for ty in tys.iter() {
|
for ty in tys.iter() {
|
||||||
dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, ty, constraints)?;
|
dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, ty, constraints)?;
|
||||||
}
|
}
|
||||||
Ok::<_, NoSolution>(())
|
Ok::<_, NoSolution>(())
|
||||||
})?,
|
})?,
|
||||||
@ -249,7 +248,14 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||||||
|
|
||||||
rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
||||||
for ty in args.as_closure().upvar_tys() {
|
for ty in args.as_closure().upvar_tys() {
|
||||||
dtorck_constraint_for_ty_inner(tcx, span, for_ty, depth + 1, ty, constraints)?;
|
dtorck_constraint_for_ty_inner(
|
||||||
|
tcx,
|
||||||
|
param_env,
|
||||||
|
span,
|
||||||
|
depth + 1,
|
||||||
|
ty,
|
||||||
|
constraints,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Ok::<_, NoSolution>(())
|
Ok::<_, NoSolution>(())
|
||||||
})?
|
})?
|
||||||
@ -278,8 +284,8 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||||||
// only take place through references with lifetimes
|
// only take place through references with lifetimes
|
||||||
// derived from lifetimes attached to the upvars and resume
|
// derived from lifetimes attached to the upvars and resume
|
||||||
// argument, and we *do* incorporate those here.
|
// argument, and we *do* incorporate those here.
|
||||||
|
let args = args.as_coroutine();
|
||||||
if !args.as_coroutine().is_valid() {
|
if !args.is_valid() {
|
||||||
// By the time this code runs, all type variables ought to
|
// By the time this code runs, all type variables ought to
|
||||||
// be fully resolved.
|
// be fully resolved.
|
||||||
tcx.sess.delay_span_bug(
|
tcx.sess.delay_span_bug(
|
||||||
@ -289,10 +295,13 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
|
|||||||
return Err(NoSolution);
|
return Err(NoSolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraints
|
// While we conservatively assume that all coroutines require drop
|
||||||
.outlives
|
// to avoid query cycles during MIR building, we can check the actual
|
||||||
.extend(args.as_coroutine().upvar_tys().iter().map(ty::GenericArg::from));
|
// witness during borrowck to avoid unnecessary liveness constraints.
|
||||||
constraints.outlives.push(args.as_coroutine().resume_ty().into());
|
if args.witness().needs_drop(tcx, param_env) {
|
||||||
|
constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from));
|
||||||
|
constraints.outlives.push(args.resume_ty().into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Adt(def, args) => {
|
ty::Adt(def, args) => {
|
||||||
|
@ -34,6 +34,7 @@ pub(crate) fn adt_dtorck_constraint(
|
|||||||
) -> Result<&DropckConstraint<'_>, NoSolution> {
|
) -> Result<&DropckConstraint<'_>, NoSolution> {
|
||||||
let def = tcx.adt_def(def_id);
|
let def = tcx.adt_def(def_id);
|
||||||
let span = tcx.def_span(def_id);
|
let span = tcx.def_span(def_id);
|
||||||
|
let param_env = tcx.param_env(def_id);
|
||||||
debug!("dtorck_constraint: {:?}", def);
|
debug!("dtorck_constraint: {:?}", def);
|
||||||
|
|
||||||
if def.is_manually_drop() {
|
if def.is_manually_drop() {
|
||||||
@ -55,7 +56,7 @@ pub(crate) fn adt_dtorck_constraint(
|
|||||||
let mut result = DropckConstraint::empty();
|
let mut result = DropckConstraint::empty();
|
||||||
for field in def.all_fields() {
|
for field in def.all_fields() {
|
||||||
let fty = tcx.type_of(field.did).instantiate_identity();
|
let fty = tcx.type_of(field.did).instantiate_identity();
|
||||||
dtorck_constraint_for_ty_inner(tcx, span, fty, 0, fty, &mut result)?;
|
dtorck_constraint_for_ty_inner(tcx, param_env, span, 0, fty, &mut result)?;
|
||||||
}
|
}
|
||||||
result.outlives.extend(tcx.destructor_constraints(def));
|
result.outlives.extend(tcx.destructor_constraints(def));
|
||||||
dedup_dtorck_constraint(&mut result);
|
dedup_dtorck_constraint(&mut result);
|
||||||
|
@ -66,6 +66,9 @@ fn has_significant_drop_raw<'tcx>(
|
|||||||
struct NeedsDropTypes<'tcx, F> {
|
struct NeedsDropTypes<'tcx, F> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
// Whether to reveal coroutine witnesses, this is set
|
||||||
|
// to `false` unless we compute `needs_drop` for a generator witness.
|
||||||
|
reveal_coroutine_witnesses: bool,
|
||||||
query_ty: Ty<'tcx>,
|
query_ty: Ty<'tcx>,
|
||||||
seen_tys: FxHashSet<Ty<'tcx>>,
|
seen_tys: FxHashSet<Ty<'tcx>>,
|
||||||
/// A stack of types left to process, and the recursion depth when we
|
/// A stack of types left to process, and the recursion depth when we
|
||||||
@ -89,6 +92,7 @@ fn new(
|
|||||||
Self {
|
Self {
|
||||||
tcx,
|
tcx,
|
||||||
param_env,
|
param_env,
|
||||||
|
reveal_coroutine_witnesses: false,
|
||||||
seen_tys,
|
seen_tys,
|
||||||
query_ty: ty,
|
query_ty: ty,
|
||||||
unchecked_tys: vec![(ty, 0)],
|
unchecked_tys: vec![(ty, 0)],
|
||||||
@ -133,9 +137,32 @@ fn next(&mut self) -> Option<NeedsDropResult<Ty<'tcx>>> {
|
|||||||
// The information required to determine whether a coroutine has drop is
|
// The information required to determine whether a coroutine has drop is
|
||||||
// computed on MIR, while this very method is used to build MIR.
|
// computed on MIR, while this very method is used to build MIR.
|
||||||
// To avoid cycles, we consider that coroutines always require drop.
|
// To avoid cycles, we consider that coroutines always require drop.
|
||||||
ty::Coroutine(..) => {
|
//
|
||||||
|
// HACK: Because we erase regions contained in the generator witness, we
|
||||||
|
// have to conservatively assume that every region captured by the
|
||||||
|
// generator has to be live when dropped. This results in a lot of
|
||||||
|
// undesirable borrowck errors. During borrowck, we call `needs_drop`
|
||||||
|
// for the generator witness and check whether any of the contained types
|
||||||
|
// 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(Err(AlwaysRequiresDrop));
|
return Some(Err(AlwaysRequiresDrop));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
EarlyBinder::bind(field_ty.ty).instantiate(tcx, args),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ if component.is_copy_modulo_regions(tcx, self.param_env) => (),
|
_ if component.is_copy_modulo_regions(tcx, self.param_env) => (),
|
||||||
|
|
||||||
@ -191,7 +218,6 @@ fn next(&mut self) -> Option<NeedsDropResult<Ty<'tcx>>> {
|
|||||||
| ty::FnPtr(..)
|
| ty::FnPtr(..)
|
||||||
| ty::Tuple(_)
|
| ty::Tuple(_)
|
||||||
| ty::Bound(..)
|
| ty::Bound(..)
|
||||||
| ty::CoroutineWitness(..)
|
|
||||||
| ty::Never
|
| ty::Never
|
||||||
| ty::Infer(_)
|
| ty::Infer(_)
|
||||||
| ty::Error(_) => {
|
| ty::Error(_) => {
|
||||||
|
@ -1,24 +1,16 @@
|
|||||||
error[E0597]: `a` does not live long enough
|
error[E0597]: `a` does not live long enough
|
||||||
--> $DIR/borrowing.rs:9:33
|
--> $DIR/borrowing.rs:9:33
|
||||||
|
|
|
|
||||||
|
LL | let _b = {
|
||||||
|
| -- borrow later stored here
|
||||||
|
LL | let a = 3;
|
||||||
LL | Pin::new(&mut || yield &a).resume(())
|
LL | Pin::new(&mut || yield &a).resume(())
|
||||||
| ----------^
|
| -- ^ borrowed value does not live long enough
|
||||||
| | |
|
| |
|
||||||
| | borrowed value does not live long enough
|
|
||||||
| value captured here by coroutine
|
| value captured here by coroutine
|
||||||
| a temporary with access to the borrow is created here ...
|
|
||||||
LL |
|
LL |
|
||||||
LL | };
|
LL | };
|
||||||
| -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for coroutine
|
| - `a` dropped here while still borrowed
|
||||||
| |
|
|
||||||
| `a` dropped here while still borrowed
|
|
||||||
|
|
|
||||||
= note: the temporary is part of an expression at the end of a block;
|
|
||||||
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
|
|
||||||
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
|
||||||
|
|
|
||||||
LL | let x = Pin::new(&mut || yield &a).resume(()); x
|
|
||||||
| +++++++ +++
|
|
||||||
|
|
||||||
error[E0597]: `a` does not live long enough
|
error[E0597]: `a` does not live long enough
|
||||||
--> $DIR/borrowing.rs:16:20
|
--> $DIR/borrowing.rs:16:20
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// edition:2021
|
// edition:2021
|
||||||
|
// check-pass
|
||||||
#![feature(coroutines)]
|
#![feature(coroutines)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -6,6 +7,5 @@ fn main() {
|
|||||||
|| {
|
|| {
|
||||||
let _c = || yield *&mut *x;
|
let _c = || yield *&mut *x;
|
||||||
|| _ = &mut *x;
|
|| _ = &mut *x;
|
||||||
//~^ cannot borrow `*x` as mutable more than once at a time
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
error[E0499]: cannot borrow `*x` as mutable more than once at a time
|
|
||||||
--> $DIR/issue-110929-coroutine-conflict-error-ice.rs:8:9
|
|
||||||
|
|
|
||||||
LL | let _c = || yield *&mut *x;
|
|
||||||
| -- -- first borrow occurs due to use of `*x` in coroutine
|
|
||||||
| |
|
|
||||||
| first mutable borrow occurs here
|
|
||||||
LL | || _ = &mut *x;
|
|
||||||
| ^^ -- second borrow occurs due to use of `*x` in closure
|
|
||||||
| |
|
|
||||||
| second mutable borrow occurs here
|
|
||||||
LL |
|
|
||||||
LL | };
|
|
||||||
| - first borrow might be used here, when `_c` is dropped and runs the destructor for coroutine
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0499`.
|
|
@ -4,10 +4,9 @@ error[E0499]: cannot borrow `thing` as mutable more than once at a time
|
|||||||
LL | gen.as_mut().resume(&mut thing);
|
LL | gen.as_mut().resume(&mut thing);
|
||||||
| ---------- first mutable borrow occurs here
|
| ---------- first mutable borrow occurs here
|
||||||
LL | gen.as_mut().resume(&mut thing);
|
LL | gen.as_mut().resume(&mut thing);
|
||||||
| ^^^^^^^^^^ second mutable borrow occurs here
|
| ------ ^^^^^^^^^^ second mutable borrow occurs here
|
||||||
LL |
|
| |
|
||||||
LL | }
|
| first borrow later used by call
|
||||||
| - first borrow might be used here, when `gen` is dropped and runs the destructor for coroutine
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user