about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRoxane <roxane.fruytier@hotmail.com>2021-02-21 10:20:40 -0500
committerRoxane <roxane.fruytier@hotmail.com>2021-03-14 17:07:59 -0400
commitb6cf070eb4c228c146ca9971cddeb034084f88de (patch)
tree25ce055aafb0d277796edcb8e62568f215080a0e
parentd4f8729c892882b16d7ce66f287818b6a66fe200 (diff)
downloadrust-b6cf070eb4c228c146ca9971cddeb034084f88de.tar.gz
rust-b6cf070eb4c228c146ca9971cddeb034084f88de.zip
Attempt to deal with nested closures properly
-rw-r--r--compiler/rustc_typeck/src/check/upvar.rs7
-rw-r--r--compiler/rustc_typeck/src/expr_use_visitor.rs40
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr110
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns-1.rs118
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns-1.stderr42
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs52
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr61
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/no_capture_with_wildcard_match.rs12
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/no_capture_with_wildcard_match.stderr32
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/struct_update_syntax.rs23
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/struct_update_syntax.stderr57
12 files changed, 560 insertions, 15 deletions
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 74e2ca51039..932f07ff1bd 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -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);
         }
     }
 
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index 45ecf30cd50..ad39b93c067 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -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);
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs
new file mode 100644
index 00000000000..db067b6c9bc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs
@@ -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();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr
new file mode 100644
index 00000000000..c9a1a32fc57
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr
@@ -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`.
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns-1.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns-1.rs
new file mode 100644
index 00000000000..757b506fd6e
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns-1.rs
@@ -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();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns-1.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns-1.stderr
new file mode 100644
index 00000000000..1ae64eb83ef
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns-1.stderr
@@ -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
+
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs
new file mode 100644
index 00000000000..6f958bb18c3
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs
@@ -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();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr
new file mode 100644
index 00000000000..1ee5d2abf13
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr
@@ -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`.
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/no_capture_with_wildcard_match.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/no_capture_with_wildcard_match.rs
new file mode 100644
index 00000000000..18803516101
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/no_capture_with_wildcard_match.rs
@@ -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 { _ => () };
+    };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/no_capture_with_wildcard_match.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/no_capture_with_wildcard_match.stderr
new file mode 100644
index 00000000000..e2e825fe942
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/no_capture_with_wildcard_match.stderr
@@ -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`.
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/struct_update_syntax.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/struct_update_syntax.rs
new file mode 100644
index 00000000000..9757860fb4c
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/struct_update_syntax.rs
@@ -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();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/struct_update_syntax.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/struct_update_syntax.stderr
new file mode 100644
index 00000000000..15d8040c46c
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/struct_update_syntax.stderr
@@ -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`.