Attempt to deal with nested closures properly

This commit is contained in:
Roxane 2021-02-21 10:20:40 -05:00
parent d4f8729c89
commit b6cf070eb4
12 changed files with 560 additions and 15 deletions

View File

@ -1153,7 +1153,6 @@ struct InferBorrowKind<'a, 'tcx> {
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
/// ```
capture_information: InferredCaptureInformation<'tcx>,
// [FIXME] RFC2229 Change Vec to FxHashSet
fake_reads: FxHashSet<Place<'tcx>>, // these need to be fake read.
}
@ -1416,9 +1415,9 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
}
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
fn fake_read(&mut self, place: PlaceWithHirId<'tcx>) {
if let PlaceBase::Upvar(_) = place.place.base {
self.fake_reads.insert(place.place);
fn fake_read(&mut self, place: Place<'tcx>) {
if let PlaceBase::Upvar(_) = place.base {
self.fake_reads.insert(place);
}
}

View File

@ -5,7 +5,7 @@
pub use self::ConsumeMode::*;
// Export these here so that Clippy can use them.
pub use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId, Projection};
pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
use rustc_hir as hir;
use rustc_hir::def::Res;
@ -54,7 +54,7 @@ pub trait Delegate<'tcx> {
fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId);
// [FIXME] RFC2229 This should also affect clippy ref: https://github.com/sexxi-goose/rust/pull/27
fn fake_read(&mut self, place: PlaceWithHirId<'tcx>);
fn fake_read(&mut self, place: Place<'tcx>);
}
#[derive(Copy, Clone, PartialEq, Debug)]
@ -558,7 +558,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat);
self.delegate.fake_read(discr_place.clone());
self.delegate.fake_read(discr_place.place.clone());
let tcx = self.tcx();
let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self;
@ -620,8 +620,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
/// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
/// closure as the DefId.
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) {
debug!("walk_captures({:?})", closure_expr);
// Over here we walk a closure that is nested inside the current body
// If the current body is a closure, then we also want to report back any fake reads,
// starting off of variables that are captured by our parent as well.
@ -635,6 +633,32 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
ty::Closure(..) | ty::Generator(..)
);
// [FIXME] RFC2229 Closures within closures don't work
if let Some(fake_reads) = self.mc.typeck_results.closure_fake_reads.get(&closure_def_id) {
for fake_read in fake_reads.iter() {
// Use this as a reference for if we should promote the fake read
match fake_read.base {
PlaceBase::Upvar(upvar_id) => {
if upvars.map_or(body_owner_is_closure, |upvars| {
!upvars.contains_key(&upvar_id.var_path.hir_id)
}) {
// The nested closure might be capturing the current (enclosing) closure's local variables.
// We check if the root variable is ever mentioned within the enclosing closure, if not
// then for the current body (if it's a closure) these aren't captures, we will ignore them.
continue;
}
}
_ => {
bug!(
"Do not know how to get HirId out of Rvalue and StaticItem {:?}",
fake_read.base
);
}
};
self.delegate.fake_read(fake_read.clone());
}
}
if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id)
{
for (var_hir_id, min_list) in min_captures.iter() {
@ -664,12 +688,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
place.projections.clone(),
);
// [FIXME] RFC2229 We want to created another loop that iterates mc.typeck_results.fake_reads()
// [FIXME] RFC2229 Add tests for nested closures
if body_owner_is_closure {
self.delegate.fake_read(place_with_id.clone());
}
match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => {
let mode = copy_or_move(&self.mc, &place_with_id);

View File

@ -0,0 +1,21 @@
#![feature(capture_disjoint_fields)]
#![feature(rustc_attrs)]
fn main() {
let _z = 9;
let t = (String::from("Hello"), String::from("World"));
let g = (String::from("Mr"), String::from("Goose"));
let a = #[rustc_capture_analysis] || {
let (_, g2) = g;
println!("{}", g2);
let c = #[rustc_capture_analysis] || {
let (_, t2) = t;
println!("{}", t2);
};
c();
};
a();
}

View File

@ -0,0 +1,110 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/destructure-pattern-closure-within-closure.rs:9:13
|
LL | let a = #[rustc_capture_analysis] || {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/destructure-pattern-closure-within-closure.rs:12:17
|
LL | let c = #[rustc_capture_analysis] || {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/destructure-pattern-closure-within-closure.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/destructure-pattern-closure-within-closure.rs:12:43
|
LL | let c = #[rustc_capture_analysis] || {
| ___________________________________________^
LL | | let (_, t2) = t;
LL | | println!("{}", t2);
LL | | };
| |_________^
|
note: Capturing t[(1, 0)] -> ByValue
--> $DIR/destructure-pattern-closure-within-closure.rs:13:27
|
LL | let (_, t2) = t;
| ^
error: Min Capture analysis includes:
--> $DIR/destructure-pattern-closure-within-closure.rs:12:43
|
LL | let c = #[rustc_capture_analysis] || {
| ___________________________________________^
LL | | let (_, t2) = t;
LL | | println!("{}", t2);
LL | | };
| |_________^
|
note: Min Capture t[(1, 0)] -> ByValue
--> $DIR/destructure-pattern-closure-within-closure.rs:13:27
|
LL | let (_, t2) = t;
| ^
error: First Pass analysis includes:
--> $DIR/destructure-pattern-closure-within-closure.rs:9:39
|
LL | let a = #[rustc_capture_analysis] || {
| _______________________________________^
LL | | let (_, g2) = g;
LL | | println!("{}", g2);
LL | | let c = #[rustc_capture_analysis] || {
... |
LL | | c();
LL | | };
| |_____^
|
note: Capturing g[(1, 0)] -> ByValue
--> $DIR/destructure-pattern-closure-within-closure.rs:10:23
|
LL | let (_, g2) = g;
| ^
note: Capturing t[(1, 0)] -> ByValue
--> $DIR/destructure-pattern-closure-within-closure.rs:13:27
|
LL | let (_, t2) = t;
| ^
error: Min Capture analysis includes:
--> $DIR/destructure-pattern-closure-within-closure.rs:9:39
|
LL | let a = #[rustc_capture_analysis] || {
| _______________________________________^
LL | | let (_, g2) = g;
LL | | println!("{}", g2);
LL | | let c = #[rustc_capture_analysis] || {
... |
LL | | c();
LL | | };
| |_____^
|
note: Min Capture g[(1, 0)] -> ByValue
--> $DIR/destructure-pattern-closure-within-closure.rs:10:23
|
LL | let (_, g2) = g;
| ^
note: Min Capture t[(1, 0)] -> ByValue
--> $DIR/destructure-pattern-closure-within-closure.rs:13:27
|
LL | let (_, t2) = t;
| ^
error: aborting due to 6 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,118 @@
//check-pass
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
#![warn(unused)]
struct Point {
x: u32,
y: u32,
}
fn test1() {
let _z = 9;
let t = (String::from("Hello"), String::from("World"));
let c = || {
let (t1, t2) = t;
println!("{} {}", t1, t2);
};
c();
}
fn test2() {
let _z = 9;
let t = (String::from("Hello"), String::from("World"));
let c = || {
let (t1, _) = t;
println!("{}", t1);
};
c();
}
fn test3() {
let _z = 9;
let t = (String::from("Hello"), String::from("World"));
let c = || {
let (_, t2) = t;
println!("{}", t2);
};
c();
}
fn test4() {
let _z = 9;
let t = (String::from("Hello"), String::from("World"));
//~^ WARN unused variable: `t`
let c = || {
let (_, _) = t;
};
c();
}
fn test5() {
let _z = 9;
let t = (String::new(), String::new());
let _c = || {
let _a = match t {
(t1, _) => t1,
};
};
}
fn test6() {
let _z = 9;
let t = (String::new(), String::new());
let _c = || {
let _a = match t {
(_, t2) => t2,
};
};
}
fn test7() {
let x = 0;
//~^ WARN unused variable: `x`
let tup = (1, 2);
//~^ WARN unused variable: `tup`
let p = Point { x: 10, y: 20 };
let c = || {
let _ = x;
let Point { x, y } = p; // 1
//~^ WARN unused variable: `x`
println!("{}", y);
let (_, _) = tup; // 2
};
c();
}
fn test8() {
let _z = 9;
let t = (String::from("Hello"), String::from("World"));
let c = || {
let (_, t) = t;
println!("{}", t);
};
c();
}
fn main() {
test1();
test2();
test3();
test4();
test5();
test6();
test7();
test8();
}

View File

@ -0,0 +1,42 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/destructure_patterns-1.rs:2:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
warning: unused variable: `t`
--> $DIR/destructure_patterns-1.rs:49:9
|
LL | let t = (String::from("Hello"), String::from("World"));
| ^ help: if this is intentional, prefix it with an underscore: `_t`
|
note: the lint level is defined here
--> $DIR/destructure_patterns-1.rs:4:9
|
LL | #![warn(unused)]
| ^^^^^^
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
warning: unused variable: `x`
--> $DIR/destructure_patterns-1.rs:88:21
|
LL | let Point { x, y } = p; // 1
| ^ help: try ignoring the field: `x: _`
warning: unused variable: `x`
--> $DIR/destructure_patterns-1.rs:80:9
|
LL | let x = 0;
| ^ help: if this is intentional, prefix it with an underscore: `_x`
warning: unused variable: `tup`
--> $DIR/destructure_patterns-1.rs:82:9
|
LL | let tup = (1, 2);
| ^^^ help: if this is intentional, prefix it with an underscore: `_tup`
warning: 5 warnings emitted

View File

@ -0,0 +1,52 @@
#![feature(capture_disjoint_fields)]
#![feature(rustc_attrs)]
struct S {
a: String,
b: String,
}
fn main() {
let t = (String::new(), String::new());
let s = S {
a: String::new(),
b: String::new(),
};
let c = #[rustc_capture_analysis] || {
let (t1, t2) = t;
};
// MIR Build
//
// Create place for the initalizer in let which is `t`
//
// I'm reading Field 1 from `t`, so apply Field projections;
//
// new place -> t[1]
//
// I'm reading Field 2 from `t`, so apply Field projections;
//
// new place -> t[2]
//
// New
// ---------
//
// I'm building something starting at `t`
//
// I read field 1 from `t`
//
// I need to use `t[1]`, therefore the place must be constructable
//
// Find the capture index for `t[1]` for this closure.
//
// I read field 2 from `t`
//
// I need to use `t[2]`, therefore the place must be constructable
//
// Find the capture index for `t[2]` for this closure.
c();
}

View File

@ -0,0 +1,61 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/destructure_patterns.rs:17:13
|
LL | let c = #[rustc_capture_analysis] || {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/destructure_patterns.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/destructure_patterns.rs:17:39
|
LL | let c = #[rustc_capture_analysis] || {
| _______________________________________^
LL | | let (t1, t2) = t;
LL | | };
| |_____^
|
note: Capturing t[(0, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:18:24
|
LL | let (t1, t2) = t;
| ^
note: Capturing t[(1, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:18:24
|
LL | let (t1, t2) = t;
| ^
error: Min Capture analysis includes:
--> $DIR/destructure_patterns.rs:17:39
|
LL | let c = #[rustc_capture_analysis] || {
| _______________________________________^
LL | | let (t1, t2) = t;
LL | | };
| |_____^
|
note: Min Capture t[(0, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:18:24
|
LL | let (t1, t2) = t;
| ^
note: Min Capture t[(1, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:18:24
|
LL | let (t1, t2) = t;
| ^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,12 @@
#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
#![feature(rustc_attrs)]
fn main() {
let foo = [1, 2, 3];
let c = #[rustc_capture_analysis] || {
//~^ ERROR: attributes on expressions are experimental
//~| ERROR: First Pass analysis includes:
match foo { _ => () };
};
}

View File

@ -0,0 +1,32 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/no_capture_with_wildcard_match.rs:7:13
|
LL | let c = #[rustc_capture_analysis] || {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/no_capture_with_wildcard_match.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/no_capture_with_wildcard_match.rs:7:39
|
LL | let c = #[rustc_capture_analysis] || {
| _______________________________________^
LL | |
LL | |
LL | | match foo { _ => () };
LL | | };
| |_____^
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,23 @@
#![feature(capture_disjoint_fields)]
#![feature(rustc_attrs)]
struct S {
a: String,
b: String,
}
fn main() {
let s = S {
a: String::new(),
b: String::new(),
};
let c = #[rustc_capture_analysis] || {
let s2 = S {
a: format!("New a"),
..s
};
};
c();
}

View File

@ -0,0 +1,57 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/struct_update_syntax.rs:15:13
|
LL | let c = #[rustc_capture_analysis] || {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/struct_update_syntax.rs:1:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
error: First Pass analysis includes:
--> $DIR/struct_update_syntax.rs:15:39
|
LL | let c = #[rustc_capture_analysis] || {
| _______________________________________^
LL | | let s2 = S {
LL | | a: format!("New a"),
LL | | ..s
LL | | };
LL | | };
| |_____^
|
note: Capturing s[(1, 0)] -> ByValue
--> $DIR/struct_update_syntax.rs:18:15
|
LL | ..s
| ^
error: Min Capture analysis includes:
--> $DIR/struct_update_syntax.rs:15:39
|
LL | let c = #[rustc_capture_analysis] || {
| _______________________________________^
LL | | let s2 = S {
LL | | a: format!("New a"),
LL | | ..s
LL | | };
LL | | };
| |_____^
|
note: Min Capture s[(1, 0)] -> ByValue
--> $DIR/struct_update_syntax.rs:18:15
|
LL | ..s
| ^
error: aborting due to 3 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.