summaryrefslogtreecommitdiffstats
path: root/src/test/ui/closures/2229_closure_analysis
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/ui/closures/2229_closure_analysis')
-rw-r--r--src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs22
-rw-r--r--src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr53
-rw-r--r--src/test/ui/closures/2229_closure_analysis/by_value.rs37
-rw-r--r--src/test/ui/closures/2229_closure_analysis/by_value.stderr58
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr68
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs28
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr56
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr56
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs31
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr53
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs29
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr48
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs24
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr48
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-enum-field.rs27
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-enums.rs62
-rw-r--r--src/test/ui/closures/2229_closure_analysis/capture-enums.stderr113
-rw-r--r--src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs50
-rw-r--r--src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr61
-rw-r--r--src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs24
-rw-r--r--src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr61
-rw-r--r--src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs77
-rw-r--r--src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr178
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs85
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr104
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs19
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr19
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs19
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr19
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs18
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr18
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs20
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr22
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs64
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr48
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs19
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr12
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs34
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs13
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr14
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs27
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs18
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs22
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs13
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs13
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr14
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs92
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr65
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs43
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs28
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr17
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs37
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr24
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs32
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr29
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs25
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs25
-rw-r--r--src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr18
-rw-r--r--src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs18
-rw-r--r--src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr48
-rw-r--r--src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs41
-rw-r--r--src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-87378.rs26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-87378.stderr48
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-87987.rs27
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-87987.stderr14
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs24
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr12
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-88476.rs62
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-88476.stderr97
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-89606.rs40
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-90465.fixed35
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-90465.rs34
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-90465.stderr26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs14
-rw-r--r--src/test/ui/closures/2229_closure_analysis/issue_88118.rs15
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs10
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/issue-87097.rs35
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/issue-87097.stderr36
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/issue-87426.rs14
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/issue-87988.rs19
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/issue-88331.rs33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/issue-88331.stderr39
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs44
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs37
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr17
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs54
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr70
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs79
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr83
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs137
-rw-r--r--src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr203
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed88
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs87
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr67
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs32
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed38
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs38
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed76
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs74
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr45
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs45
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs10
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr12
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs34
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs37
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed25
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/macro.rs25
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr22
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed47
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs46
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr41
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed51
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs50
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed157
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs153
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr118
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs81
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/old_name.rs9
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/old_name.stderr10
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed69
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/precise.rs67
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr55
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs104
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed229
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs220
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr214
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs13
-rw-r--r--src/test/ui/closures/2229_closure_analysis/move_closure.rs222
-rw-r--r--src/test/ui/closures/2229_closure_analysis/move_closure.stderr462
-rw-r--r--src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs39
-rw-r--r--src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr48
-rw-r--r--src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr48
-rw-r--r--src/test/ui/closures/2229_closure_analysis/nested-closure.rs53
-rw-r--r--src/test/ui/closures/2229_closure_analysis/nested-closure.stderr106
-rw-r--r--src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs34
-rw-r--r--src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr36
-rw-r--r--src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs27
-rw-r--r--src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr48
-rw-r--r--src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs101
-rw-r--r--src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr228
-rw-r--r--src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs58
-rw-r--r--src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout13
-rw-r--r--src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout13
-rw-r--r--src/test/ui/closures/2229_closure_analysis/repr_packed.rs101
-rw-r--r--src/test/ui/closures/2229_closure_analysis/repr_packed.stderr161
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/box.rs92
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs26
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs24
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs20
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs27
-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.stderr21
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs119
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr39
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs23
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs11
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs23
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs37
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs42
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs16
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs19
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs59
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs47
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs30
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs93
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs33
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs31
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs28
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs54
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs43
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs36
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs49
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs43
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs47
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs11
-rw-r--r--src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs39
-rw-r--r--src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr56
-rw-r--r--src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs62
-rw-r--r--src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr93
-rw-r--r--src/test/ui/closures/2229_closure_analysis/wild_patterns.rs73
-rw-r--r--src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr138
199 files changed, 10233 insertions, 0 deletions
diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs
new file mode 100644
index 000000000..2bcbd792e
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs
@@ -0,0 +1,22 @@
+// edition:2021
+#![feature(rustc_attrs)]
+
+// Ensure that capture analysis results in arrays being completely captured.
+fn main() {
+ let mut m = [1, 2, 3, 4, 5];
+
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ m[0] += 10;
+ //~^ NOTE: Capturing m[] -> MutBorrow
+ //~| NOTE: Min Capture m[] -> MutBorrow
+ m[1] += 40;
+ //~^ NOTE: Capturing m[] -> MutBorrow
+ };
+
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr
new file mode 100644
index 000000000..129b26456
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr
@@ -0,0 +1,53 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/arrays-completely-captured.rs:8:17
+ |
+LL | let mut 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
+
+error: First Pass analysis includes:
+ --> $DIR/arrays-completely-captured.rs:11:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | m[0] += 10;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing m[] -> MutBorrow
+ --> $DIR/arrays-completely-captured.rs:14:9
+ |
+LL | m[0] += 10;
+ | ^
+note: Capturing m[] -> MutBorrow
+ --> $DIR/arrays-completely-captured.rs:17:9
+ |
+LL | m[1] += 40;
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/arrays-completely-captured.rs:11:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | m[0] += 10;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture m[] -> MutBorrow
+ --> $DIR/arrays-completely-captured.rs:14:9
+ |
+LL | m[0] += 10;
+ | ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.rs b/src/test/ui/closures/2229_closure_analysis/by_value.rs
new file mode 100644
index 000000000..d8d3bbee2
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/by_value.rs
@@ -0,0 +1,37 @@
+// edition:2021
+
+// Test that we handle derferences properly when only some of the captures are being moved with
+// `capture_disjoint_fields` enabled.
+#![feature(rustc_attrs)]
+
+#[derive(Debug, Default)]
+struct SomeLargeType;
+struct MuchLargerType([SomeLargeType; 32]);
+
+// Ensure that we don't capture any derefs when moving captures into the closures,
+// i.e. only data from the enclosing stack is moved.
+fn big_box() {
+ let s = MuchLargerType(Default::default());
+ let b = Box::new(s);
+ let t = (b, 10);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ let p = t.0.0;
+ //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+ //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+ println!("{} {:?}", t.1, p);
+ //~^ NOTE: Capturing t[(1, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow
+ };
+
+ c();
+}
+
+fn main() {
+ big_box();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.stderr b/src/test/ui/closures/2229_closure_analysis/by_value.stderr
new file mode 100644
index 000000000..097462253
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/by_value.stderr
@@ -0,0 +1,58 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/by_value.rs:18: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
+
+error: First Pass analysis includes:
+ --> $DIR/by_value.rs:21:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let p = t.0.0;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+ --> $DIR/by_value.rs:24:17
+ |
+LL | let p = t.0.0;
+ | ^^^^^
+note: Capturing t[(1, 0)] -> ImmBorrow
+ --> $DIR/by_value.rs:27:29
+ |
+LL | println!("{} {:?}", t.1, p);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/by_value.rs:21:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let p = t.0.0;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0)] -> ByValue
+ --> $DIR/by_value.rs:24:17
+ |
+LL | let p = t.0.0;
+ | ^^^^^
+note: Min Capture t[(1, 0)] -> ImmBorrow
+ --> $DIR/by_value.rs:27:29
+ |
+LL | println!("{} {:?}", t.1, p);
+ | ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs
new file mode 100644
index 000000000..dc53b3176
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let p = Point { x: 10, y: 10 };
+ let q = Point { x: 10, y: 10 };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ println!("{:?}", p);
+ //~^ NOTE: Capturing p[] -> ImmBorrow
+ //~| NOTE: Min Capture p[] -> ImmBorrow
+ println!("{:?}", p.x);
+ //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+
+ println!("{:?}", q.x);
+ //~^ NOTE: Capturing q[(0, 0)] -> ImmBorrow
+ println!("{:?}", q);
+ //~^ NOTE: Capturing q[] -> ImmBorrow
+ //~| NOTE: Min Capture q[] -> ImmBorrow
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr
new file mode 100644
index 000000000..fceafb9c8
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr
@@ -0,0 +1,68 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/capture-analysis-1.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
+
+error: First Pass analysis includes:
+ --> $DIR/capture-analysis-1.rs:18:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{:?}", p);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing p[] -> ImmBorrow
+ --> $DIR/capture-analysis-1.rs:21:26
+ |
+LL | println!("{:?}", p);
+ | ^
+note: Capturing p[(0, 0)] -> ImmBorrow
+ --> $DIR/capture-analysis-1.rs:24:26
+ |
+LL | println!("{:?}", p.x);
+ | ^^^
+note: Capturing q[(0, 0)] -> ImmBorrow
+ --> $DIR/capture-analysis-1.rs:27:26
+ |
+LL | println!("{:?}", q.x);
+ | ^^^
+note: Capturing q[] -> ImmBorrow
+ --> $DIR/capture-analysis-1.rs:29:26
+ |
+LL | println!("{:?}", q);
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/capture-analysis-1.rs:18:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{:?}", p);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[] -> ImmBorrow
+ --> $DIR/capture-analysis-1.rs:21:26
+ |
+LL | println!("{:?}", p);
+ | ^
+note: Min Capture q[] -> ImmBorrow
+ --> $DIR/capture-analysis-1.rs:29:26
+ |
+LL | println!("{:?}", q);
+ | ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs
new file mode 100644
index 000000000..99d12f8d8
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs
@@ -0,0 +1,28 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Point {
+ x: String,
+ y: i32,
+}
+
+fn main() {
+ let mut p = Point { x: String::new(), y: 10 };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ let _x = p.x;
+ //~^ NOTE: Capturing p[(0, 0)] -> ByValue
+ //~| NOTE: p[] captured as ByValue here
+ println!("{:?}", p);
+ //~^ NOTE: Capturing p[] -> ImmBorrow
+ //~| NOTE: Min Capture p[] -> ByValue
+ //~| NOTE: p[] used here
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr
new file mode 100644
index 000000000..cb44ca266
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr
@@ -0,0 +1,56 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/capture-analysis-2.rs:14: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
+
+error: First Pass analysis includes:
+ --> $DIR/capture-analysis-2.rs:17:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let _x = p.x;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing p[(0, 0)] -> ByValue
+ --> $DIR/capture-analysis-2.rs:20:18
+ |
+LL | let _x = p.x;
+ | ^^^
+note: Capturing p[] -> ImmBorrow
+ --> $DIR/capture-analysis-2.rs:23:26
+ |
+LL | println!("{:?}", p);
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/capture-analysis-2.rs:17:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let _x = p.x;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[] -> ByValue
+ --> $DIR/capture-analysis-2.rs:20:18
+ |
+LL | let _x = p.x;
+ | ^^^ p[] captured as ByValue here
+...
+LL | println!("{:?}", p);
+ | ^ p[] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs
new file mode 100644
index 000000000..3f337097d
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Child {
+ c: String,
+ d: String,
+}
+
+#[derive(Debug)]
+struct Parent {
+ b: Child,
+}
+
+fn main() {
+ let mut a = Parent { b: Child {c: String::new(), d: String::new()} };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ let _x = a.b.c;
+ //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ByValue
+ //~| NOTE: a[(0, 0)] captured as ByValue here
+ println!("{:?}", a.b);
+ //~^ NOTE: Capturing a[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture a[(0, 0)] -> ByValue
+ //~| NOTE: a[(0, 0)] used here
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr
new file mode 100644
index 000000000..71e7bdc35
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr
@@ -0,0 +1,56 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/capture-analysis-3.rs:19: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
+
+error: First Pass analysis includes:
+ --> $DIR/capture-analysis-3.rs:22:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let _x = a.b.c;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing a[(0, 0),(0, 0)] -> ByValue
+ --> $DIR/capture-analysis-3.rs:25:18
+ |
+LL | let _x = a.b.c;
+ | ^^^^^
+note: Capturing a[(0, 0)] -> ImmBorrow
+ --> $DIR/capture-analysis-3.rs:28:26
+ |
+LL | println!("{:?}", a.b);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/capture-analysis-3.rs:22:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let _x = a.b.c;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture a[(0, 0)] -> ByValue
+ --> $DIR/capture-analysis-3.rs:25:18
+ |
+LL | let _x = a.b.c;
+ | ^^^^^ a[(0, 0)] captured as ByValue here
+...
+LL | println!("{:?}", a.b);
+ | ^^^ a[(0, 0)] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs
new file mode 100644
index 000000000..bc46ec997
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs
@@ -0,0 +1,31 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Child {
+ c: String,
+ d: String,
+}
+
+#[derive(Debug)]
+struct Parent {
+ b: Child,
+}
+
+fn main() {
+ let mut a = Parent { b: Child {c: String::new(), d: String::new()} };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ let _x = a.b;
+ //~^ NOTE: Capturing a[(0, 0)] -> ByValue
+ //~| NOTE: Min Capture a[(0, 0)] -> ByValue
+ println!("{:?}", a.b.c);
+ //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ImmBorrow
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr
new file mode 100644
index 000000000..7e6e625bc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr
@@ -0,0 +1,53 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/capture-analysis-4.rs:19: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
+
+error: First Pass analysis includes:
+ --> $DIR/capture-analysis-4.rs:22:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let _x = a.b;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing a[(0, 0)] -> ByValue
+ --> $DIR/capture-analysis-4.rs:25:18
+ |
+LL | let _x = a.b;
+ | ^^^
+note: Capturing a[(0, 0),(0, 0)] -> ImmBorrow
+ --> $DIR/capture-analysis-4.rs:28:26
+ |
+LL | println!("{:?}", a.b.c);
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/capture-analysis-4.rs:22:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let _x = a.b;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture a[(0, 0)] -> ByValue
+ --> $DIR/capture-analysis-4.rs:25:18
+ |
+LL | let _x = a.b;
+ | ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs
new file mode 100644
index 000000000..6fd151553
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs
@@ -0,0 +1,29 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let mut p = Point { x: 10, y: 10 };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ println!("{}", p.x);
+ //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
+ };
+
+ // `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
+ let py = &mut p.y;
+
+ c();
+ *py = 20;
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr
new file mode 100644
index 000000000..0f64ecf3a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/capture-disjoint-field-struct.rs:13: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
+
+error: First Pass analysis includes:
+ --> $DIR/capture-disjoint-field-struct.rs:16:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", p.x);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing p[(0, 0)] -> ImmBorrow
+ --> $DIR/capture-disjoint-field-struct.rs:19:24
+ |
+LL | println!("{}", p.x);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/capture-disjoint-field-struct.rs:16:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", p.x);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[(0, 0)] -> ImmBorrow
+ --> $DIR/capture-disjoint-field-struct.rs:19:24
+ |
+LL | println!("{}", p.x);
+ | ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs
new file mode 100644
index 000000000..8d3bb3262
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs
@@ -0,0 +1,24 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+fn main() {
+ let mut t = (10, 10);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ println!("{}", t.0);
+ //~^ NOTE: Capturing t[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow
+ };
+
+ // `c` only captures t.0, therefore mutating t.1 is allowed.
+ let t1 = &mut t.1;
+
+ c();
+ *t1 = 20;
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr
new file mode 100644
index 000000000..a8ca9622a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/capture-disjoint-field-tuple.rs:8: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
+
+error: First Pass analysis includes:
+ --> $DIR/capture-disjoint-field-tuple.rs:11:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", t.0);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0)] -> ImmBorrow
+ --> $DIR/capture-disjoint-field-tuple.rs:14:24
+ |
+LL | println!("{}", t.0);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/capture-disjoint-field-tuple.rs:11:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", t.0);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0)] -> ImmBorrow
+ --> $DIR/capture-disjoint-field-tuple.rs:14:24
+ |
+LL | println!("{}", t.0);
+ | ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enum-field.rs b/src/test/ui/closures/2229_closure_analysis/capture-enum-field.rs
new file mode 100644
index 000000000..bbe3aa31a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-enum-field.rs
@@ -0,0 +1,27 @@
+// edition:2021
+// run-pass
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Color {
+ RGB(u8, u8, u8),
+}
+
+fn main() {
+ let mut color = Color::RGB(0, 0, 0);
+ let mut red = |v| {
+ let Color::RGB(ref mut r, _, _) = color;
+ *r = v;
+ };
+ let mut green = |v| {
+ let Color::RGB(_, ref mut g, _) = color;
+ *g = v;
+ };
+ let mut blue = |v| {
+ let Color::RGB(_, _, ref mut b) = color;
+ *b = v;
+ };
+ red(1);
+ green(2);
+ blue(3);
+ assert_eq!(Color::RGB(1, 2, 3), color);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs
new file mode 100644
index 000000000..322ae99b8
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs
@@ -0,0 +1,62 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+enum Info {
+ Point(i32, i32, String),
+ Meta(String, Vec<(i32, i32)>)
+}
+
+fn multi_variant_enum() {
+ let point = Info::Point(10, -10, "1".into());
+
+ let vec = Vec::new();
+ let meta = Info::Meta("meta".into(), vec);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ if let Info::Point(_, _, str) = point {
+ //~^ NOTE: Capturing point[] -> ImmBorrow
+ //~| NOTE: Capturing point[(2, 0)] -> ByValue
+ //~| NOTE: Min Capture point[] -> ByValue
+ println!("{}", str);
+ }
+
+ if let Info::Meta(_, v) = meta {
+ //~^ NOTE: Capturing meta[] -> ImmBorrow
+ //~| NOTE: Capturing meta[(1, 1)] -> ByValue
+ //~| NOTE: Min Capture meta[] -> ByValue
+ println!("{:?}", v);
+ }
+ };
+
+ c();
+}
+
+enum SingleVariant {
+ Point(i32, i32, String),
+}
+
+fn single_variant_enum() {
+ let point = SingleVariant::Point(10, -10, "1".into());
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ let SingleVariant::Point(_, _, str) = point;
+ //~^ NOTE: Capturing point[(2, 0)] -> ByValue
+ //~| NOTE: Min Capture point[(2, 0)] -> ByValue
+ println!("{}", str);
+ };
+
+ c();
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr
new file mode 100644
index 000000000..8a6ba8444
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr
@@ -0,0 +1,113 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/capture-enums.rs:16: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/capture-enums.rs:47: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
+
+error: First Pass analysis includes:
+ --> $DIR/capture-enums.rs:19:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | if let Info::Point(_, _, str) = point {
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+note: Capturing point[] -> ImmBorrow
+ --> $DIR/capture-enums.rs:22:41
+ |
+LL | if let Info::Point(_, _, str) = point {
+ | ^^^^^
+note: Capturing point[(2, 0)] -> ByValue
+ --> $DIR/capture-enums.rs:22:41
+ |
+LL | if let Info::Point(_, _, str) = point {
+ | ^^^^^
+note: Capturing meta[] -> ImmBorrow
+ --> $DIR/capture-enums.rs:29:35
+ |
+LL | if let Info::Meta(_, v) = meta {
+ | ^^^^
+note: Capturing meta[(1, 1)] -> ByValue
+ --> $DIR/capture-enums.rs:29:35
+ |
+LL | if let Info::Meta(_, v) = meta {
+ | ^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/capture-enums.rs:19:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | if let Info::Point(_, _, str) = point {
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+note: Min Capture point[] -> ByValue
+ --> $DIR/capture-enums.rs:22:41
+ |
+LL | if let Info::Point(_, _, str) = point {
+ | ^^^^^
+note: Min Capture meta[] -> ByValue
+ --> $DIR/capture-enums.rs:29:35
+ |
+LL | if let Info::Meta(_, v) = meta {
+ | ^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/capture-enums.rs:50:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let SingleVariant::Point(_, _, str) = point;
+... |
+LL | | println!("{}", str);
+LL | | };
+ | |_____^
+ |
+note: Capturing point[(2, 0)] -> ByValue
+ --> $DIR/capture-enums.rs:53:47
+ |
+LL | let SingleVariant::Point(_, _, str) = point;
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/capture-enums.rs:50:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let SingleVariant::Point(_, _, str) = point;
+... |
+LL | | println!("{}", str);
+LL | | };
+ | |_____^
+ |
+note: Min Capture point[(2, 0)] -> ByValue
+ --> $DIR/capture-enums.rs:53:47
+ |
+LL | let SingleVariant::Point(_, _, str) = point;
+ | ^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs
new file mode 100644
index 000000000..3341166e2
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs
@@ -0,0 +1,50 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+#[derive(Debug)]
+struct Line {
+ p: Point,
+ q: Point
+}
+#[derive(Debug)]
+struct Plane {
+ a: Line,
+ b: Line,
+}
+
+fn main() {
+ let mut p = Plane {
+ a: Line {
+ p: Point { x: 1,y: 2 },
+ q: Point { x: 3,y: 4 },
+ },
+ b: Line {
+ p: Point { x: 1,y: 2 },
+ q: Point { x: 3,y: 4 },
+ }
+ };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let x = &p.a.p.x;
+ //~^ NOTE: Capturing p[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+ p.b.q.y = 9;
+ //~^ NOTE: Capturing p[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+ //~| NOTE: p[] captured as MutBorrow here
+ println!("{:?}", p);
+ //~^ NOTE: Capturing p[] -> ImmBorrow
+ //~| NOTE: Min Capture p[] -> MutBorrow
+ //~| NOTE: p[] used here
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr
new file mode 100644
index 000000000..29e1af043
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr
@@ -0,0 +1,61 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/deep-multilevel-struct.rs:34: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
+
+error: First Pass analysis includes:
+ --> $DIR/deep-multilevel-struct.rs:37:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let x = &p.a.p.x;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing p[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+ --> $DIR/deep-multilevel-struct.rs:40:18
+ |
+LL | let x = &p.a.p.x;
+ | ^^^^^^^
+note: Capturing p[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+ --> $DIR/deep-multilevel-struct.rs:42:9
+ |
+LL | p.b.q.y = 9;
+ | ^^^^^^^
+note: Capturing p[] -> ImmBorrow
+ --> $DIR/deep-multilevel-struct.rs:45:26
+ |
+LL | println!("{:?}", p);
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/deep-multilevel-struct.rs:37:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let x = &p.a.p.x;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[] -> MutBorrow
+ --> $DIR/deep-multilevel-struct.rs:42:9
+ |
+LL | p.b.q.y = 9;
+ | ^^^^^^^ p[] captured as MutBorrow here
+...
+LL | println!("{:?}", p);
+ | ^ p[] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs
new file mode 100644
index 000000000..34b0132f3
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs
@@ -0,0 +1,24 @@
+// edition:2021
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+fn main() {
+ let mut t = (((1,2),(3,4)),((5,6),(7,8)));
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let x = &t.0.0.0;
+ //~^ NOTE: Capturing t[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+ t.1.1.1 = 9;
+ //~^ NOTE: Capturing t[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+ //~| NOTE: t[] captured as MutBorrow here
+ println!("{:?}", t);
+ //~^ NOTE: Min Capture t[] -> MutBorrow
+ //~| NOTE: Capturing t[] -> ImmBorrow
+ //~| NOTE: t[] used here
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr
new file mode 100644
index 000000000..e91751676
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr
@@ -0,0 +1,61 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/deep-multilevel-tuple.rs:8: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
+
+error: First Pass analysis includes:
+ --> $DIR/deep-multilevel-tuple.rs:11:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let x = &t.0.0.0;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+ --> $DIR/deep-multilevel-tuple.rs:14:18
+ |
+LL | let x = &t.0.0.0;
+ | ^^^^^^^
+note: Capturing t[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+ --> $DIR/deep-multilevel-tuple.rs:16:9
+ |
+LL | t.1.1.1 = 9;
+ | ^^^^^^^
+note: Capturing t[] -> ImmBorrow
+ --> $DIR/deep-multilevel-tuple.rs:19:26
+ |
+LL | println!("{:?}", t);
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/deep-multilevel-tuple.rs:11:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let x = &t.0.0.0;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[] -> MutBorrow
+ --> $DIR/deep-multilevel-tuple.rs:16:9
+ |
+LL | t.1.1.1 = 9;
+ | ^^^^^^^ t[] captured as MutBorrow here
+...
+LL | println!("{:?}", t);
+ | ^ t[] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs
new file mode 100644
index 000000000..6c65a7bf8
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs
@@ -0,0 +1,77 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test to ensure Index projections are handled properly during capture analysis
+// The array should be moved in entirety, even though only some elements are used.
+fn arrays() {
+ let arr: [String; 5] = [format!("A"), format!("B"), format!("C"), format!("D"), format!("E")];
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let [a, b, .., e] = arr;
+ //~^ NOTE: Capturing arr[Index] -> ByValue
+ //~| NOTE: Capturing arr[Index] -> ByValue
+ //~| NOTE: Capturing arr[Index] -> ByValue
+ //~| NOTE: Min Capture arr[] -> ByValue
+ assert_eq!(a, "A");
+ assert_eq!(b, "B");
+ assert_eq!(e, "E");
+ };
+
+ c();
+}
+
+struct Point {
+ x: i32,
+ y: i32,
+ id: String,
+}
+
+fn structs() {
+ let mut p = Point { x: 10, y: 10, id: String::new() };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let Point { x: ref mut x, y: _, id: moved_id } = p;
+ //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow
+ //~| NOTE: Capturing p[(2, 0)] -> ByValue
+ //~| NOTE: Min Capture p[(0, 0)] -> MutBorrow
+ //~| NOTE: Min Capture p[(2, 0)] -> ByValue
+
+ println!("{}, {}", x, moved_id);
+ };
+ c();
+}
+
+fn tuples() {
+ let mut t = (10, String::new(), (String::new(), 42));
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let (ref mut x, ref ref_str, (moved_s, _)) = t;
+ //~^ NOTE: Capturing t[(0, 0)] -> MutBorrow
+ //~| NOTE: Capturing t[(1, 0)] -> ImmBorrow
+ //~| NOTE: Capturing t[(2, 0),(0, 0)] -> ByValue
+ //~| NOTE: Min Capture t[(0, 0)] -> MutBorrow
+ //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture t[(2, 0),(0, 0)] -> ByValue
+
+ println!("{}, {} {}", x, ref_str, moved_s);
+ };
+ c();
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr
new file mode 100644
index 000000000..44fbe6d81
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr
@@ -0,0 +1,178 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/destructure_patterns.rs:10: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/destructure_patterns.rs:38: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/destructure_patterns.rs:58: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
+
+error: First Pass analysis includes:
+ --> $DIR/destructure_patterns.rs:13:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let [a, b, .., e] = arr;
+... |
+LL | | assert_eq!(e, "E");
+LL | | };
+ | |_____^
+ |
+note: Capturing arr[Index] -> ByValue
+ --> $DIR/destructure_patterns.rs:16:29
+ |
+LL | let [a, b, .., e] = arr;
+ | ^^^
+note: Capturing arr[Index] -> ByValue
+ --> $DIR/destructure_patterns.rs:16:29
+ |
+LL | let [a, b, .., e] = arr;
+ | ^^^
+note: Capturing arr[Index] -> ByValue
+ --> $DIR/destructure_patterns.rs:16:29
+ |
+LL | let [a, b, .., e] = arr;
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/destructure_patterns.rs:13:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let [a, b, .., e] = arr;
+... |
+LL | | assert_eq!(e, "E");
+LL | | };
+ | |_____^
+ |
+note: Min Capture arr[] -> ByValue
+ --> $DIR/destructure_patterns.rs:16:29
+ |
+LL | let [a, b, .., e] = arr;
+ | ^^^
+
+error: First Pass analysis includes:
+ --> $DIR/destructure_patterns.rs:41:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let Point { x: ref mut x, y: _, id: moved_id } = p;
+... |
+LL | | println!("{}, {}", x, moved_id);
+LL | | };
+ | |_____^
+ |
+note: Capturing p[(0, 0)] -> MutBorrow
+ --> $DIR/destructure_patterns.rs:44:58
+ |
+LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
+ | ^
+note: Capturing p[(2, 0)] -> ByValue
+ --> $DIR/destructure_patterns.rs:44:58
+ |
+LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/destructure_patterns.rs:41:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let Point { x: ref mut x, y: _, id: moved_id } = p;
+... |
+LL | | println!("{}, {}", x, moved_id);
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[(0, 0)] -> MutBorrow
+ --> $DIR/destructure_patterns.rs:44:58
+ |
+LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
+ | ^
+note: Min Capture p[(2, 0)] -> ByValue
+ --> $DIR/destructure_patterns.rs:44:58
+ |
+LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
+ | ^
+
+error: First Pass analysis includes:
+ --> $DIR/destructure_patterns.rs:61:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t;
+... |
+LL | | println!("{}, {} {}", x, ref_str, moved_s);
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0)] -> MutBorrow
+ --> $DIR/destructure_patterns.rs:64:54
+ |
+LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
+ | ^
+note: Capturing t[(1, 0)] -> ImmBorrow
+ --> $DIR/destructure_patterns.rs:64:54
+ |
+LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
+ | ^
+note: Capturing t[(2, 0),(0, 0)] -> ByValue
+ --> $DIR/destructure_patterns.rs:64:54
+ |
+LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/destructure_patterns.rs:61:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t;
+... |
+LL | | println!("{}, {} {}", x, ref_str, moved_s);
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0)] -> MutBorrow
+ --> $DIR/destructure_patterns.rs:64:54
+ |
+LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
+ | ^
+note: Min Capture t[(1, 0)] -> ImmBorrow
+ --> $DIR/destructure_patterns.rs:64:54
+ |
+LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
+ | ^
+note: Min Capture t[(2, 0),(0, 0)] -> ByValue
+ --> $DIR/destructure_patterns.rs:64:54
+ |
+LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
+ | ^
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs
new file mode 100644
index 000000000..93131b2ac
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs
@@ -0,0 +1,85 @@
+// edition:2021
+
+// Test that arrays are completely captured by closures by relying on the borrow check diagnostics
+
+fn arrays_1() {
+ let mut arr = [1, 2, 3, 4, 5];
+
+ let mut c = || {
+ arr[0] += 10;
+ };
+
+ // c will capture `arr` completely, therefore another index into the
+ // array can't be modified here
+ arr[1] += 10;
+ //~^ ERROR: cannot use `arr` because it was mutably borrowed
+ //~| ERROR: cannot use `arr[_]` because it was mutably borrowed
+ c();
+}
+
+fn arrays_2() {
+ let mut arr = [1, 2, 3, 4, 5];
+
+ let c = || {
+ println!("{:#?}", &arr[3..4]);
+ };
+
+ // c will capture `arr` completely, therefore another index into the
+ // array can't be modified here
+ arr[1] += 10;
+ //~^ ERROR: cannot assign to `arr[_]` because it is borrowed
+ c();
+}
+
+fn arrays_3() {
+ let mut arr = [1, 2, 3, 4, 5];
+
+ let c = || {
+ println!("{}", arr[3]);
+ };
+
+ // c will capture `arr` completely, therefore another index into the
+ // array can't be modified here
+ arr[1] += 10;
+ //~^ ERROR: cannot assign to `arr[_]` because it is borrowed
+ c();
+}
+
+fn arrays_4() {
+ let mut arr = [1, 2, 3, 4, 5];
+
+ let mut c = || {
+ arr[1] += 10;
+ };
+
+ // c will capture `arr` completely, therefore we cannot borrow another index
+ // into the array.
+ println!("{}", arr[3]);
+ //~^ ERROR: cannot use `arr` because it was mutably borrowed
+ //~| ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable
+
+ c();
+}
+
+fn arrays_5() {
+ let mut arr = [1, 2, 3, 4, 5];
+
+ let mut c = || {
+ arr[1] += 10;
+ };
+
+ // c will capture `arr` completely, therefore we cannot borrow other indecies
+ // into the array.
+ println!("{:#?}", &arr[3..2]);
+ //~^ ERROR: cannot borrow `arr` as immutable because it is also borrowed as mutable
+
+ c();
+}
+
+fn main() {
+ arrays_1();
+ arrays_2();
+ arrays_3();
+ arrays_4();
+ arrays_5();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr
new file mode 100644
index 000000000..4f41060dc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr
@@ -0,0 +1,104 @@
+error[E0503]: cannot use `arr` because it was mutably borrowed
+ --> $DIR/arrays.rs:14:5
+ |
+LL | let mut c = || {
+ | -- borrow of `arr` occurs here
+LL | arr[0] += 10;
+ | --- borrow occurs due to use of `arr` in closure
+...
+LL | arr[1] += 10;
+ | ^^^^^^ use of borrowed `arr`
+...
+LL | c();
+ | - borrow later used here
+
+error[E0503]: cannot use `arr[_]` because it was mutably borrowed
+ --> $DIR/arrays.rs:14:5
+ |
+LL | let mut c = || {
+ | -- borrow of `arr` occurs here
+LL | arr[0] += 10;
+ | --- borrow occurs due to use of `arr` in closure
+...
+LL | arr[1] += 10;
+ | ^^^^^^^^^^^^ use of borrowed `arr`
+...
+LL | c();
+ | - borrow later used here
+
+error[E0506]: cannot assign to `arr[_]` because it is borrowed
+ --> $DIR/arrays.rs:29:5
+ |
+LL | let c = || {
+ | -- borrow of `arr[_]` occurs here
+LL | println!("{:#?}", &arr[3..4]);
+ | --- borrow occurs due to use in closure
+...
+LL | arr[1] += 10;
+ | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
+LL |
+LL | c();
+ | - borrow later used here
+
+error[E0506]: cannot assign to `arr[_]` because it is borrowed
+ --> $DIR/arrays.rs:43:5
+ |
+LL | let c = || {
+ | -- borrow of `arr[_]` occurs here
+LL | println!("{}", arr[3]);
+ | --- borrow occurs due to use in closure
+...
+LL | arr[1] += 10;
+ | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
+LL |
+LL | c();
+ | - borrow later used here
+
+error[E0503]: cannot use `arr` because it was mutably borrowed
+ --> $DIR/arrays.rs:57:20
+ |
+LL | let mut c = || {
+ | -- borrow of `arr` occurs here
+LL | arr[1] += 10;
+ | --- borrow occurs due to use of `arr` in closure
+...
+LL | println!("{}", arr[3]);
+ | ^^^^^^ use of borrowed `arr`
+...
+LL | c();
+ | - borrow later used here
+
+error[E0502]: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable
+ --> $DIR/arrays.rs:57:20
+ |
+LL | let mut c = || {
+ | -- mutable borrow occurs here
+LL | arr[1] += 10;
+ | --- first borrow occurs due to use of `arr` in closure
+...
+LL | println!("{}", arr[3]);
+ | ^^^^^^ immutable borrow occurs here
+...
+LL | c();
+ | - mutable borrow later used here
+ |
+ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable
+ --> $DIR/arrays.rs:73:24
+ |
+LL | let mut c = || {
+ | -- mutable borrow occurs here
+LL | arr[1] += 10;
+ | --- first borrow occurs due to use of `arr` in closure
+...
+LL | println!("{:#?}", &arr[3..2]);
+ | ^^^ immutable borrow occurs here
+...
+LL | c();
+ | - mutable borrow later used here
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0502, E0503, E0506.
+For more information about an error, try `rustc --explain E0502`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs
new file mode 100644
index 000000000..3664d76c2
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs
@@ -0,0 +1,19 @@
+// edition:2021
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+fn main() {
+ let mut p = Point {x: 1, y: 2 };
+
+ let y = &mut p.y;
+ let mut c = || {
+ //~^ ERROR cannot borrow `p` as mutable more than once at a time
+ let x = &mut p.x;
+ println!("{:?}", p);
+ };
+ c();
+ *y+=1;
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr
new file mode 100644
index 000000000..341d2bc65
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr
@@ -0,0 +1,19 @@
+error[E0499]: cannot borrow `p` as mutable more than once at a time
+ --> $DIR/borrowck-1.rs:12:17
+ |
+LL | let y = &mut p.y;
+ | -------- first mutable borrow occurs here
+LL | let mut c = || {
+ | ^^ second mutable borrow occurs here
+LL |
+LL | let x = &mut p.x;
+ | --- capture is mutable because of use here
+LL | println!("{:?}", p);
+ | - second borrow occurs due to use of `p` in closure
+...
+LL | *y+=1;
+ | ----- first borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs
new file mode 100644
index 000000000..ae416bab6
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs
@@ -0,0 +1,19 @@
+// edition:2021
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+fn main() {
+ let mut p = Point {x: 1, y: 2 };
+
+ let y = &p.y;
+ let mut c = || {
+ //~^ ERROR cannot borrow `p` as mutable because it is also borrowed as immutable
+ println!("{:?}", p);
+ let x = &mut p.x;
+ };
+ c();
+ println!("{}", y);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr
new file mode 100644
index 000000000..584bb862b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr
@@ -0,0 +1,19 @@
+error[E0502]: cannot borrow `p` as mutable because it is also borrowed as immutable
+ --> $DIR/borrowck-2.rs:12:17
+ |
+LL | let y = &p.y;
+ | ---- immutable borrow occurs here
+LL | let mut c = || {
+ | ^^ mutable borrow occurs here
+LL |
+LL | println!("{:?}", p);
+ | - second borrow occurs due to use of `p` in closure
+LL | let x = &mut p.x;
+ | --- capture is mutable because of use here
+...
+LL | println!("{}", y);
+ | - immutable borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0502`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs
new file mode 100644
index 000000000..bdd6cb79b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs
@@ -0,0 +1,18 @@
+// edition:2021
+
+#[derive(Debug)]
+struct Point {
+ x: String,
+ y: String,
+}
+fn main() {
+ let mut c = {
+ let mut p = Point {x: "1".to_string(), y: "2".to_string() };
+ || {
+ let x = &mut p.x;
+ println!("{:?}", p);
+ //~^ ERROR `p` does not live long enough
+ }
+ };
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr
new file mode 100644
index 000000000..dab1809a3
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr
@@ -0,0 +1,18 @@
+error[E0597]: `p` does not live long enough
+ --> $DIR/borrowck-3.rs:13:29
+ |
+LL | let mut c = {
+ | ----- borrow later stored here
+LL | let mut p = Point {x: "1".to_string(), y: "2".to_string() };
+LL | || {
+ | -- value captured here
+LL | let x = &mut p.x;
+LL | println!("{:?}", p);
+ | ^ borrowed value does not live long enough
+...
+LL | };
+ | - `p` dropped here while still borrowed
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs
new file mode 100644
index 000000000..a2290d850
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs
@@ -0,0 +1,20 @@
+// edition:2021
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+fn foo () -> impl FnMut()->() {
+ let mut p = Point {x: 1, y: 2 };
+ let mut c = || {
+ //~^ ERROR closure may outlive the current function, but it borrows `p`
+ p.x+=5;
+ println!("{:?}", p);
+ };
+ c
+}
+fn main() {
+ let c = foo();
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr
new file mode 100644
index 000000000..46379a381
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr
@@ -0,0 +1,22 @@
+error[E0373]: closure may outlive the current function, but it borrows `p`, which is owned by the current function
+ --> $DIR/borrowck-4.rs:10:17
+ |
+LL | let mut c = || {
+ | ^^ may outlive borrowed value `p`
+...
+LL | println!("{:?}", p);
+ | - `p` is borrowed here
+ |
+note: closure is returned here
+ --> $DIR/borrowck-4.rs:15:5
+ |
+LL | c
+ | ^
+help: to force the closure to take ownership of `p` (and any other referenced variables), use the `move` keyword
+ |
+LL | let mut c = move || {
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0373`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs
new file mode 100644
index 000000000..5ff7b1242
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs
@@ -0,0 +1,26 @@
+// edition:2021
+
+
+
+// Tests that two closures cannot simultaneously have mutable
+// and immutable access to the variable. Issue #6801.
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn a() {
+ let mut p = Point {x: 3, y:4};
+ let c2 = || p.y * 5;
+ let c1 = || {
+ //~^ ERROR cannot borrow `p` as mutable because it is also borrowed as immutable
+ dbg!(&p);
+ p.x = 4;
+ };
+ drop(c2);
+}
+
+fn main() {
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr
new file mode 100644
index 000000000..5f1dae297
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr
@@ -0,0 +1,21 @@
+error[E0502]: cannot borrow `p` as mutable because it is also borrowed as immutable
+ --> $DIR/borrowck-closures-mut-and-imm.rs:17:14
+ |
+LL | let c2 = || p.y * 5;
+ | -- --- first borrow occurs due to use of `p.y` in closure
+ | |
+ | immutable borrow occurs here
+LL | let c1 = || {
+ | ^^ mutable borrow occurs here
+LL |
+LL | dbg!(&p);
+ | - second borrow occurs due to use of `p` in closure
+LL | p.x = 4;
+ | --- capture is mutable because of use here
+LL | };
+LL | drop(c2);
+ | -- immutable borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0502`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs
new file mode 100644
index 000000000..a110fa4e2
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.rs
@@ -0,0 +1,64 @@
+// edition:2021
+
+// Test borrow checker when we precise capture when using boxes
+
+struct MetaData { x: String, name: String }
+struct Data { m: MetaData }
+struct BoxedData(Box<Data>);
+struct EvenMoreBoxedData(Box<BoxedData>);
+
+// Check diagnostics when the same path is mutated both inside and outside the closure
+fn box_1() {
+ let m = MetaData { x: format!("x"), name: format!("name") };
+ let d = Data { m };
+ let b = BoxedData(Box::new(d));
+ let mut e = EvenMoreBoxedData(Box::new(b));
+
+ let mut c = || {
+ e.0.0.m.x = format!("not-x");
+ };
+
+ e.0.0.m.x = format!("not-x");
+ //~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed
+ c();
+}
+
+// Check diagnostics when a path is mutated inside a closure while attempting to read it outside
+// the closure.
+fn box_2() {
+ let m = MetaData { x: format!("x"), name: format!("name") };
+ let d = Data { m };
+ let b = BoxedData(Box::new(d));
+ let mut e = EvenMoreBoxedData(Box::new(b));
+
+ let mut c = || {
+ e.0.0.m.x = format!("not-x");
+ };
+
+ println!("{}", e.0.0.m.x);
+ //~^ ERROR: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable
+ c();
+}
+
+// Check diagnostics when a path is read inside a closure while attempting to mutate it outside
+// the closure.
+fn box_3() {
+ let m = MetaData { x: format!("x"), name: format!("name") };
+ let d = Data { m };
+ let b = BoxedData(Box::new(d));
+ let mut e = EvenMoreBoxedData(Box::new(b));
+
+ let c = || {
+ println!("{}", e.0.0.m.x);
+ };
+
+ e.0.0.m.x = format!("not-x");
+ //~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed
+ c();
+}
+
+fn main() {
+ box_1();
+ box_2();
+ box_3();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr
new file mode 100644
index 000000000..f8b178752
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/box.stderr
@@ -0,0 +1,48 @@
+error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed
+ --> $DIR/box.rs:21:5
+ |
+LL | let mut c = || {
+ | -- borrow of `e.0.0.m.x` occurs here
+LL | e.0.0.m.x = format!("not-x");
+ | --------- borrow occurs due to use in closure
+...
+LL | e.0.0.m.x = format!("not-x");
+ | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
+LL |
+LL | c();
+ | - borrow later used here
+
+error[E0502]: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable
+ --> $DIR/box.rs:38:20
+ |
+LL | let mut c = || {
+ | -- mutable borrow occurs here
+LL | e.0.0.m.x = format!("not-x");
+ | --------- first borrow occurs due to use of `e.0.0.m.x` in closure
+...
+LL | println!("{}", e.0.0.m.x);
+ | ^^^^^^^^^ immutable borrow occurs here
+LL |
+LL | c();
+ | - mutable borrow later used here
+ |
+ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed
+ --> $DIR/box.rs:55:5
+ |
+LL | let c = || {
+ | -- borrow of `e.0.0.m.x` occurs here
+LL | println!("{}", e.0.0.m.x);
+ | --------- borrow occurs due to use in closure
+...
+LL | e.0.0.m.x = format!("not-x");
+ | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
+LL |
+LL | c();
+ | - borrow later used here
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0502, E0506.
+For more information about an error, try `rustc --explain E0502`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
new file mode 100644
index 000000000..77effcb00
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
@@ -0,0 +1,19 @@
+// edition:2021
+
+// Test that if we deref an immutable borrow to access a Place,
+// then we can't mutate the final place.
+
+fn main() {
+ let mut x = (format!(""), format!("X2"));
+ let mut y = (&x, "Y");
+ let z = (&mut y, "Z");
+
+ // `x.0` is mutable but we access `x` via `*z.0.0`, which is an immutable reference and
+ // therefore can't be mutated.
+ let mut c = || {
+ //~^ ERROR: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference
+ z.0.0.0 = format!("X1");
+ };
+
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
new file mode 100644
index 000000000..38c530b80
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
@@ -0,0 +1,12 @@
+error[E0596]: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference
+ --> $DIR/cant-mutate-imm-borrow.rs:13:17
+ |
+LL | let mut c = || {
+ | ^^ cannot borrow as mutable
+LL |
+LL | z.0.0.0 = format!("X1");
+ | ------- mutable borrow occurs due to use of `*z.0.0` in closure
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs
new file mode 100644
index 000000000..25ee9a149
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs
@@ -0,0 +1,34 @@
+// edition:2021
+
+// Ensure that diagnostics for mutability error (because the root variable
+// isn't mutable) work with `capture_disjoint_fields` enabled.
+
+fn mut_error_struct() {
+ let x = (10, 10);
+ let y = (x, 10);
+ let z = (y, 10);
+
+ let mut c = || {
+ z.0.0.0 = 20;
+ //~^ ERROR: cannot assign to `z.0.0.0`, as it is not declared as mutable
+ };
+
+ c();
+}
+
+fn mut_error_box() {
+ let x = (10, 10);
+ let bx = Box::new(x);
+
+ let mut c = || {
+ bx.0 = 20;
+ //~^ ERROR: cannot assign to `*bx.0`, as it is not declared as mutable
+ };
+
+ c();
+}
+
+fn main() {
+ mut_error_struct();
+ mut_error_box();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr
new file mode 100644
index 000000000..98414fa8a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr
@@ -0,0 +1,21 @@
+error[E0594]: cannot assign to `z.0.0.0`, as it is not declared as mutable
+ --> $DIR/cant-mutate-imm.rs:12:9
+ |
+LL | let z = (y, 10);
+ | - help: consider changing this to be mutable: `mut z`
+...
+LL | z.0.0.0 = 20;
+ | ^^^^^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `*bx.0`, as it is not declared as mutable
+ --> $DIR/cant-mutate-imm.rs:24:9
+ |
+LL | let bx = Box::new(x);
+ | -- help: consider changing this to be mutable: `mut bx`
+...
+LL | bx.0 = 20;
+ | ^^^^^^^^^ cannot assign
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs
new file mode 100644
index 000000000..f3be542e4
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs
@@ -0,0 +1,13 @@
+// edition:2021
+
+// Test that array access is not stored as part of closure kind origin
+
+fn expect_fn<F: Fn()>(_f: F) {}
+
+fn main() {
+ let s = [format!("s"), format!("s")];
+ let c = || { //~ ERROR expected a closure that implements the `Fn`
+ let [_, _s] = s;
+ };
+ expect_fn(c);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr
new file mode 100644
index 000000000..bcde35983
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr
@@ -0,0 +1,14 @@
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
+ --> $DIR/closure-origin-array-diagnostics.rs:9:13
+ |
+LL | let c = || {
+ | ^^ this closure implements `FnOnce`, not `Fn`
+LL | let [_, _s] = s;
+ | - closure is `FnOnce` because it moves the variable `s` out of its environment
+LL | };
+LL | expect_fn(c);
+ | --------- the requirement to implement `Fn` derives from here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0525`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs
new file mode 100644
index 000000000..aa85b55b1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs
@@ -0,0 +1,27 @@
+// edition:2021
+
+// Check that precise paths are being reported back in the error message.
+
+enum MultiVariant {
+ Point(i32, i32),
+ Meta(i32)
+}
+
+fn main() {
+ let mut point = MultiVariant::Point(10, -10,);
+
+ let mut meta = MultiVariant::Meta(1);
+
+ let c = || {
+ if let MultiVariant::Point(ref mut x, _) = point {
+ *x += 1;
+ }
+
+ if let MultiVariant::Meta(ref mut v) = meta {
+ *v += 1;
+ }
+ };
+
+ let a = c;
+ let b = c; //~ ERROR use of moved value: `c` [E0382]
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr
new file mode 100644
index 000000000..83d282aad
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `c`
+ --> $DIR/closure-origin-multi-variant-diagnostics.rs:26:13
+ |
+LL | let a = c;
+ | - value moved here
+LL | let b = c;
+ | ^ value used here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
+ --> $DIR/closure-origin-multi-variant-diagnostics.rs:16:52
+ |
+LL | if let MultiVariant::Point(ref mut x, _) = point {
+ | ^^^^^
+help: consider mutably borrowing `c`
+ |
+LL | let a = &mut c;
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs
new file mode 100644
index 000000000..bedb103cc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs
@@ -0,0 +1,18 @@
+// edition:2021
+
+
+enum SingleVariant {
+ Point(i32, i32),
+}
+
+fn main() {
+ let mut point = SingleVariant::Point(10, -10);
+
+ let c = || {
+ let SingleVariant::Point(ref mut x, _) = point;
+ *x += 1;
+ };
+
+ let b = c;
+ let a = c; //~ ERROR use of moved value: `c` [E0382]
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr
new file mode 100644
index 000000000..46323b752
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `c`
+ --> $DIR/closure-origin-single-variant-diagnostics.rs:17:13
+ |
+LL | let b = c;
+ | - value moved here
+LL | let a = c;
+ | ^ value used here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
+ --> $DIR/closure-origin-single-variant-diagnostics.rs:12:50
+ |
+LL | let SingleVariant::Point(ref mut x, _) = point;
+ | ^^^^^
+help: consider mutably borrowing `c`
+ |
+LL | let b = &mut c;
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs
new file mode 100644
index 000000000..3277a83c4
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs
@@ -0,0 +1,22 @@
+// edition:2021
+
+// Check that precise paths are being reported back in the error message.
+
+struct Y {
+ y: X
+}
+
+struct X {
+ a: u32,
+ b: u32,
+}
+
+fn main() {
+ let mut x = Y { y: X { a: 5, b: 0 } };
+ let hello = || {
+ x.y.a += 1;
+ };
+
+ let b = hello;
+ let c = hello; //~ ERROR use of moved value: `hello` [E0382]
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr
new file mode 100644
index 000000000..25029cc7b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `hello`
+ --> $DIR/closure-origin-struct-diagnostics.rs:21:13
+ |
+LL | let b = hello;
+ | ----- value moved here
+LL | let c = hello;
+ | ^^^^^ value used here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.y.a` out of its environment
+ --> $DIR/closure-origin-struct-diagnostics.rs:17:9
+ |
+LL | x.y.a += 1;
+ | ^^^^^
+help: consider mutably borrowing `hello`
+ |
+LL | let b = &mut hello;
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs
new file mode 100644
index 000000000..dc3a57ae7
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs
@@ -0,0 +1,13 @@
+// edition:2021
+
+// Check that precise paths are being reported back in the error message.
+
+fn main() {
+ let mut x = (5, 0);
+ let hello = || {
+ x.0 += 1;
+ };
+
+ let b = hello;
+ let c = hello; //~ ERROR use of moved value: `hello` [E0382]
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr
new file mode 100644
index 000000000..06ef7baf9
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `hello`
+ --> $DIR/closure-origin-tuple-diagnostics-1.rs:12:13
+ |
+LL | let b = hello;
+ | ----- value moved here
+LL | let c = hello;
+ | ^^^^^ value used here after move
+ |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.0` out of its environment
+ --> $DIR/closure-origin-tuple-diagnostics-1.rs:8:9
+ |
+LL | x.0 += 1;
+ | ^^^
+help: consider mutably borrowing `hello`
+ |
+LL | let b = &mut hello;
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs
new file mode 100644
index 000000000..fa1328013
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs
@@ -0,0 +1,13 @@
+// edition:2021
+
+struct S(String, String);
+
+fn expect_fn<F: Fn()>(_f: F) {}
+
+fn main() {
+ let s = S(format!("s"), format!("s"));
+ let c = || { //~ ERROR expected a closure that implements the `Fn`
+ let s = s.1;
+ };
+ expect_fn(c);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr
new file mode 100644
index 000000000..df33c4f1f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr
@@ -0,0 +1,14 @@
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
+ --> $DIR/closure-origin-tuple-diagnostics.rs:9:13
+ |
+LL | let c = || {
+ | ^^ this closure implements `FnOnce`, not `Fn`
+LL | let s = s.1;
+ | --- closure is `FnOnce` because it moves the variable `s.1` out of its environment
+LL | };
+LL | expect_fn(c);
+ | --------- the requirement to implement `Fn` derives from here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0525`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs
new file mode 100644
index 000000000..3399bc001
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.rs
@@ -0,0 +1,92 @@
+// edition:2021
+
+// check-pass
+#![allow(unreachable_code)]
+#![warn(unused)]
+#![allow(dead_code)]
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+pub fn f() {
+ let mut a = 1;
+ let mut c = Point{ x:1, y:0 };
+
+ // Captured by value, but variable is dead on entry.
+ (move || {
+ // This will not trigger a warning for unused variable as
+ // c.x will be treated as a Non-tracked place
+ c.x = 1;
+ println!("{}", c.x);
+ a = 1; //~ WARN value captured by `a` is never read
+ println!("{}", a);
+ })();
+
+ // Read and written to, but never actually used.
+ (move || {
+ // This will not trigger a warning for unused variable as
+ // c.x will be treated as a Non-tracked place
+ c.x += 1;
+ a += 1; //~ WARN unused variable: `a`
+ })();
+
+ (move || {
+ println!("{}", c.x);
+ // Value is read by closure itself on later invocations.
+ // This will not trigger a warning for unused variable as
+ // c.x will be treated as a Non-tracked place
+ c.x += 1;
+ println!("{}", a);
+ a += 1;
+ })();
+ let b = Box::new(42);
+ (move || {
+ println!("{}", c.x);
+ // Never read because this is FnOnce closure.
+ // This will not trigger a warning for unused variable as
+ // c.x will be treated as a Non-tracked place
+ c.x += 1;
+ println!("{}", a);
+ a += 1; //~ WARN value assigned to `a` is never read
+ drop(b);
+ })();
+}
+
+#[derive(Debug)]
+struct MyStruct<'a> {
+ x: Option<& 'a str>,
+ y: i32,
+}
+
+pub fn nested() {
+ let mut a : Option<& str>;
+ a = None;
+ let mut b : Option<& str>;
+ b = None;
+ let mut d = MyStruct{ x: None, y: 1};
+ let mut e = MyStruct{ x: None, y: 1};
+ (|| {
+ (|| {
+ // This will not trigger a warning for unused variable as
+ // d.x will be treated as a Non-tracked place
+ d.x = Some("d1");
+ d.x = Some("d2");
+ a = Some("d1"); //~ WARN value assigned to `a` is never read
+ a = Some("d2");
+ })();
+ (move || {
+ // This will not trigger a warning for unused variable as
+ //e.x will be treated as a Non-tracked place
+ e.x = Some("e1");
+ e.x = Some("e2");
+ b = Some("e1"); //~ WARN value assigned to `b` is never read
+ //~| WARN unused variable: `b`
+ b = Some("e2"); //~ WARN value assigned to `b` is never read
+ })();
+ })();
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr
new file mode 100644
index 000000000..7e767cba3
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr
@@ -0,0 +1,65 @@
+warning: value captured by `a` is never read
+ --> $DIR/liveness.rs:24:9
+ |
+LL | a = 1;
+ | ^
+ |
+note: the lint level is defined here
+ --> $DIR/liveness.rs:5:9
+ |
+LL | #![warn(unused)]
+ | ^^^^^^
+ = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]`
+ = help: did you mean to capture by reference instead?
+
+warning: unused variable: `a`
+ --> $DIR/liveness.rs:33:9
+ |
+LL | a += 1;
+ | ^
+ |
+ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+ = help: did you mean to capture by reference instead?
+
+warning: value assigned to `a` is never read
+ --> $DIR/liveness.rs:53:9
+ |
+LL | a += 1;
+ | ^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: value assigned to `a` is never read
+ --> $DIR/liveness.rs:77:13
+ |
+LL | a = Some("d1");
+ | ^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: value assigned to `b` is never read
+ --> $DIR/liveness.rs:85:13
+ |
+LL | b = Some("e1");
+ | ^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: value assigned to `b` is never read
+ --> $DIR/liveness.rs:87:13
+ |
+LL | b = Some("e2");
+ | ^
+ |
+ = help: maybe it is overwritten before being read?
+
+warning: unused variable: `b`
+ --> $DIR/liveness.rs:85:13
+ |
+LL | b = Some("e1");
+ | ^
+ |
+ = help: did you mean to capture by reference instead?
+
+warning: 7 warnings emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs
new file mode 100644
index 000000000..465c9476b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs
@@ -0,0 +1,43 @@
+// edition:2021
+
+// check-pass
+#![warn(unused)]
+#![allow(dead_code)]
+
+#[derive(Debug)]
+struct MyStruct {
+ a: i32,
+ b: i32,
+}
+
+pub fn unintentional_copy_one() {
+ let mut a = 1;
+ let mut last = MyStruct{ a: 1, b: 1};
+ let mut f = move |s| {
+ // This will not trigger a warning for unused variable
+ // as last.a will be treated as a Non-tracked place
+ last.a = s;
+ a = s;
+ //~^ WARN value assigned to `a` is never read
+ //~| WARN unused variable: `a`
+ };
+ f(2);
+ f(3);
+ f(4);
+}
+
+pub fn unintentional_copy_two() {
+ let mut a = 1;
+ let mut sum = MyStruct{ a: 1, b: 0};
+ (1..10).for_each(move |x| {
+ // This will not trigger a warning for unused variable
+ // as sum.b will be treated as a Non-tracked place
+ sum.b += x;
+ a += x; //~ WARN unused variable: `a`
+ });
+}
+
+fn main() {
+ unintentional_copy_one();
+ unintentional_copy_two();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr
new file mode 100644
index 000000000..2ac801b49
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr
@@ -0,0 +1,33 @@
+warning: value assigned to `a` is never read
+ --> $DIR/liveness_unintentional_copy.rs:20:9
+ |
+LL | a = s;
+ | ^
+ |
+note: the lint level is defined here
+ --> $DIR/liveness_unintentional_copy.rs:4:9
+ |
+LL | #![warn(unused)]
+ | ^^^^^^
+ = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]`
+ = help: maybe it is overwritten before being read?
+
+warning: unused variable: `a`
+ --> $DIR/liveness_unintentional_copy.rs:20:9
+ |
+LL | a = s;
+ | ^
+ |
+ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+ = help: did you mean to capture by reference instead?
+
+warning: unused variable: `a`
+ --> $DIR/liveness_unintentional_copy.rs:36:9
+ |
+LL | a += x;
+ | ^
+ |
+ = help: did you mean to capture by reference instead?
+
+warning: 3 warnings emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs
new file mode 100644
index 000000000..fa73ff23f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs
@@ -0,0 +1,28 @@
+// edition:2021
+
+// Test that when a borrow checker diagnostics are emitted, it's as precise
+// as the capture by the closure.
+
+#![allow(unused)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+struct Wrapper {
+ p: Point,
+}
+
+fn main() {
+ let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+ let mut c = || {
+ w.p.x += 20;
+ };
+
+ let py = &mut w.p.x;
+ //~^ ERROR: cannot borrow `w.p.x` as mutable more than once at a time
+ c();
+
+ *py = 20
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr
new file mode 100644
index 000000000..ac4c9c937
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr
@@ -0,0 +1,17 @@
+error[E0499]: cannot borrow `w.p.x` as mutable more than once at a time
+ --> $DIR/multilevel-path.rs:23:14
+ |
+LL | let mut c = || {
+ | -- first mutable borrow occurs here
+LL | w.p.x += 20;
+ | ----- first borrow occurs due to use of `w.p.x` in closure
+...
+LL | let py = &mut w.p.x;
+ | ^^^^^^^^^^ second mutable borrow occurs here
+LL |
+LL | c();
+ | - first borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
new file mode 100644
index 000000000..3d5a31e8b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
@@ -0,0 +1,37 @@
+// edition:2021
+
+// Test that we can't mutate a place if we need to deref an imm-borrow
+// to reach it.
+
+fn imm_mut_ref() {
+ let mut x = String::new();
+ let y = String::new();
+ let mref_x = &mut x;
+ let ref_mref_x = &mref_x;
+
+ let c = || {
+ //~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
+ **ref_mref_x = y;
+ };
+
+ c();
+}
+
+fn mut_imm_ref() {
+ let x = String::new();
+ let y = String::new();
+ let mut ref_x = &x;
+ let mref_ref_x = &mut ref_x;
+
+ let c = || {
+ //~^ ERROR: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
+ **mref_ref_x = y;
+ };
+
+ c();
+}
+
+fn main() {
+ imm_mut_ref();
+ mut_imm_ref();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
new file mode 100644
index 000000000..481d7e585
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
@@ -0,0 +1,24 @@
+error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
+ --> $DIR/mut_ref.rs:12:13
+ |
+LL | let ref_mref_x = &mref_x;
+ | ------- help: consider changing this to be a mutable reference: `&mut mref_x`
+LL |
+LL | let c = || {
+ | ^^ `ref_mref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+LL |
+LL | **ref_mref_x = y;
+ | ------------ mutable borrow occurs due to use of `**ref_mref_x` in closure
+
+error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
+ --> $DIR/mut_ref.rs:26:13
+ |
+LL | let c = || {
+ | ^^ cannot borrow as mutable
+LL |
+LL | **mref_ref_x = y;
+ | ------------ mutable borrow occurs due to use of `**mref_ref_x` in closure
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs
new file mode 100644
index 000000000..1488f3296
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs
@@ -0,0 +1,32 @@
+// edition:2021
+
+// Given how the closure desugaring is implemented (at least at the time of writing this test),
+// we don't need to truncate the captured path to a reference into a packed-struct if the field
+// being referenced will be moved into the closure, since it's safe to move out a field from a
+// packed-struct.
+//
+// However to avoid surprises for the user, or issues when the closure is
+// inlined we will truncate the capture to access just the struct regardless of if the field
+// might get moved into the closure.
+//
+// It is possible for someone to try writing the code that relies on the desugaring to create a ref
+// into a packed-struct. Here we test that the compiler still detects that case.
+fn test_missing_unsafe_warning_on_repr_packed() {
+ #[repr(packed)]
+ struct Foo { x: String }
+
+ let foo = Foo { x: String::new() };
+
+ let c = || {
+ println!("{}", foo.x);
+ //~^ ERROR: reference to packed field is unaligned
+ //~| WARNING: this was previously accepted by the compiler but is being phased out
+ let _z = foo.x;
+ };
+
+ c();
+}
+
+fn main() {
+ test_missing_unsafe_warning_on_repr_packed();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr
new file mode 100644
index 000000000..93abbecf4
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr
@@ -0,0 +1,29 @@
+error: reference to packed field is unaligned
+ --> $DIR/repr_packed.rs:21:24
+ |
+LL | println!("{}", foo.x);
+ | ^^^^^
+ |
+ = note: `#[deny(unaligned_references)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
+ = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
+ = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
+ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+Future incompatibility report: Future breakage diagnostic:
+error: reference to packed field is unaligned
+ --> $DIR/repr_packed.rs:21:24
+ |
+LL | println!("{}", foo.x);
+ | ^^^^^
+ |
+ = note: `#[deny(unaligned_references)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
+ = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
+ = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
+ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs
new file mode 100644
index 000000000..ed2d9a3de
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs
@@ -0,0 +1,25 @@
+// edition:2021
+
+// Test that borrow checker error is accurate and that min capture pass of the
+// closure analysis is working as expected.
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let mut p = Point { x: 10, y: 20 };
+
+ // `p` is captured via mutable borrow.
+ let mut c = || {
+ p.x += 10;
+ println!("{:?}", p);
+ };
+
+
+ println!("{:?}", p);
+ //~^ ERROR: cannot borrow `p` as immutable because it is also borrowed as mutable
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr
new file mode 100644
index 000000000..06157b2af
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr
@@ -0,0 +1,21 @@
+error[E0502]: cannot borrow `p` as immutable because it is also borrowed as mutable
+ --> $DIR/simple-struct-min-capture.rs:22:22
+ |
+LL | let mut c = || {
+ | -- mutable borrow occurs here
+LL | p.x += 10;
+ | --- capture is mutable because of use here
+LL | println!("{:?}", p);
+ | - first borrow occurs due to use of `p` in closure
+...
+LL | println!("{:?}", p);
+ | ^ immutable borrow occurs here
+LL |
+LL | c();
+ | - mutable borrow later used here
+ |
+ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0502`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs
new file mode 100644
index 000000000..46b54846e
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs
@@ -0,0 +1,25 @@
+// edition:2021
+
+// Test that we point to the correct location that results a union being captured.
+// Union is special because it can't be disjointly captured.
+
+union A {
+ y: u32,
+ x: (),
+}
+
+fn main() {
+ let mut a = A { y: 1 };
+ let mut c = || {
+ //~^ borrow of `a.y` occurs here
+ let _ = unsafe { &a.y };
+ let _ = &mut a;
+ //~^ borrow occurs due to use in closure
+ let _ = unsafe { &mut a.y };
+ };
+ a.y = 1;
+ //~^ cannot assign to `a.y` because it is borrowed [E0506]
+ //~| assignment to borrowed `a.y` occurs here
+ c();
+ //~^ borrow later used here
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr
new file mode 100644
index 000000000..7c34e2336
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr
@@ -0,0 +1,18 @@
+error[E0506]: cannot assign to `a.y` because it is borrowed
+ --> $DIR/union.rs:20:5
+ |
+LL | let mut c = || {
+ | -- borrow of `a.y` occurs here
+...
+LL | let _ = &mut a;
+ | - borrow occurs due to use in closure
+...
+LL | a.y = 1;
+ | ^^^^^^^ assignment to borrowed `a.y` occurs here
+...
+LL | c();
+ | - borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs
new file mode 100644
index 000000000..269cf76e6
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs
@@ -0,0 +1,18 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+fn main() {
+ let s = format!("s");
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("This uses new capture analyysis to capture s={}", s);
+ //~^ NOTE: Capturing s[] -> ImmBorrow
+ //~| NOTE: Min Capture s[] -> ImmBorrow
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr
new file mode 100644
index 000000000..b936c5ee3
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/feature-gate-capture_disjoint_fields.rs:8: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
+
+error: First Pass analysis includes:
+ --> $DIR/feature-gate-capture_disjoint_fields.rs:11:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("This uses new capture analyysis to capture s={}", s);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing s[] -> ImmBorrow
+ --> $DIR/feature-gate-capture_disjoint_fields.rs:14:69
+ |
+LL | println!("This uses new capture analyysis to capture s={}", s);
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/feature-gate-capture_disjoint_fields.rs:11:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("This uses new capture analyysis to capture s={}", s);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture s[] -> ImmBorrow
+ --> $DIR/feature-gate-capture_disjoint_fields.rs:14:69
+ |
+LL | println!("This uses new capture analyysis to capture s={}", s);
+ | ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs
new file mode 100644
index 000000000..bfa3ebcd6
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs
@@ -0,0 +1,41 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+struct Filter {
+ div: i32,
+}
+impl Filter {
+ fn allowed(&self, x: i32) -> bool {
+ x % self.div == 1
+ }
+}
+
+struct Data {
+ filter: Filter,
+ list: Vec<i32>,
+}
+impl Data {
+ fn update(&mut self) {
+ // The closure passed to filter only captures self.filter,
+ // therefore mutating self.list is allowed.
+ self.list.retain(
+ #[rustc_capture_analysis]
+ |v| self.filter.allowed(*v),
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ //~| NOTE: Capturing self[Deref,(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture self[Deref,(0, 0)] -> ImmBorrow
+ );
+ }
+}
+
+fn main() {
+ let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() };
+
+ for i in 1..10 {
+ d.list.push(i);
+ }
+
+ d.update();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr
new file mode 100644
index 000000000..10e0d076b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr
@@ -0,0 +1,26 @@
+error: First Pass analysis includes:
+ --> $DIR/filter-on-struct-member.rs:24:13
+ |
+LL | |v| self.filter.allowed(*v),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: Capturing self[Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/filter-on-struct-member.rs:24:17
+ |
+LL | |v| self.filter.allowed(*v),
+ | ^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/filter-on-struct-member.rs:24:13
+ |
+LL | |v| self.filter.allowed(*v),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: Min Capture self[Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/filter-on-struct-member.rs:24:17
+ |
+LL | |v| self.filter.allowed(*v),
+ | ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87378.rs b/src/test/ui/closures/2229_closure_analysis/issue-87378.rs
new file mode 100644
index 000000000..75901a571
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-87378.rs
@@ -0,0 +1,26 @@
+#![feature(rustc_attrs)]
+
+// edition:2021
+
+// Test that any precise capture on a union is truncated because it's unsafe to do so.
+
+union Union {
+ value: u64,
+}
+
+fn main() {
+ let u = Union { value: 42 };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ unsafe { u.value }
+ //~^ NOTE: Capturing u[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture u[] -> ImmBorrow
+ };
+
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr b/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr
new file mode 100644
index 000000000..16c3f7c97
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/issue-87378.rs:14: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
+
+error: First Pass analysis includes:
+ --> $DIR/issue-87378.rs:17:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | unsafe { u.value }
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing u[(0, 0)] -> ImmBorrow
+ --> $DIR/issue-87378.rs:20:17
+ |
+LL | unsafe { u.value }
+ | ^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/issue-87378.rs:17:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | unsafe { u.value }
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture u[] -> ImmBorrow
+ --> $DIR/issue-87378.rs:20:17
+ |
+LL | unsafe { u.value }
+ | ^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87987.rs b/src/test/ui/closures/2229_closure_analysis/issue-87987.rs
new file mode 100644
index 000000000..d26343c33
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-87987.rs
@@ -0,0 +1,27 @@
+// run-pass
+// edition:2021
+
+struct Props {
+ field_1: u32, //~ WARNING: fields `field_1` and `field_2` are never read
+ field_2: u32,
+}
+
+fn main() {
+ // Test 1
+ let props_2 = Props { field_1: 1, field_2: 1 };
+
+ let _ = || {
+ let _: Props = props_2;
+ };
+
+ // Test 2
+ let mut arr = [1, 3, 4, 5];
+
+ let mref = &mut arr;
+
+ let _c = || match arr {
+ [_, _, _, _] => println!("A"),
+ };
+
+ println!("{:#?}", mref);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr b/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr
new file mode 100644
index 000000000..5696a010c
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-87987.stderr
@@ -0,0 +1,14 @@
+warning: fields `field_1` and `field_2` are never read
+ --> $DIR/issue-87987.rs:5:5
+ |
+LL | struct Props {
+ | ----- fields in this struct
+LL | field_1: u32,
+ | ^^^^^^^
+LL | field_2: u32,
+ | ^^^^^^^
+ |
+ = note: `#[warn(dead_code)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs
new file mode 100644
index 000000000..0cfb1a55b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs
@@ -0,0 +1,24 @@
+// edition:2021
+// run-pass
+#![feature(if_let_guard)]
+#[allow(unused_must_use)]
+#[allow(dead_code)]
+
+fn print_error_count(registry: &Registry) {
+ |x: &Registry| {
+ match &x {
+ Registry if let _ = registry.try_find_description() => { }
+ //~^ WARNING: irrefutable `if let` guard pattern
+ _ => {}
+ }
+ };
+}
+
+struct Registry;
+impl Registry {
+ pub fn try_find_description(&self) {
+ unimplemented!()
+ }
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr
new file mode 100644
index 000000000..15689023d
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr
@@ -0,0 +1,12 @@
+warning: irrefutable `if let` guard pattern
+ --> $DIR/issue-88118-2.rs:10:29
+ |
+LL | Registry if let _ = registry.try_find_description() => { }
+ | ^
+ |
+ = note: `#[warn(irrefutable_let_patterns)]` on by default
+ = note: this pattern will always match, so the guard is useless
+ = help: consider removing the guard and adding a `let` inside the match arm
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88476.rs b/src/test/ui/closures/2229_closure_analysis/issue-88476.rs
new file mode 100644
index 000000000..f5906d306
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-88476.rs
@@ -0,0 +1,62 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test that we can't move out of struct that impls `Drop`.
+
+
+use std::rc::Rc;
+
+// Test that we restrict precision when moving not-`Copy` types, if any of the parent paths
+// implement `Drop`. This is to ensure that we don't move out of a type that implements Drop.
+pub fn test1() {
+ struct Foo(Rc<i32>);
+
+ impl Drop for Foo {
+ fn drop(self: &mut Foo) {}
+ }
+
+ let f = Foo(Rc::new(1));
+ let x = #[rustc_capture_analysis] move || {
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ //~| ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{:?}", f.0);
+ //~^ NOTE: Capturing f[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture f[] -> ByValue
+ };
+
+ x();
+}
+
+// Test that we don't restrict precision when moving `Copy` types(i.e. when copying),
+// even if any of the parent paths implement `Drop`.
+fn test2() {
+ struct Character {
+ hp: u32,
+ name: String,
+ }
+
+ impl Drop for Character {
+ fn drop(&mut self) {}
+ }
+
+ let character = Character { hp: 100, name: format!("A") };
+
+ let c = #[rustc_capture_analysis] move || {
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ //~| ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{}", character.hp)
+ //~^ NOTE: Capturing character[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture character[(0, 0)] -> ByValue
+ };
+
+ c();
+
+ println!("{}", character.name);
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88476.stderr b/src/test/ui/closures/2229_closure_analysis/issue-88476.stderr
new file mode 100644
index 000000000..c7c9ecbbb
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-88476.stderr
@@ -0,0 +1,97 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/issue-88476.rs:20:13
+ |
+LL | let x = #[rustc_capture_analysis] move || {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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/issue-88476.rs:47:13
+ |
+LL | let c = #[rustc_capture_analysis] move || {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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: First Pass analysis includes:
+ --> $DIR/issue-88476.rs:20:39
+ |
+LL | let x = #[rustc_capture_analysis] move || {
+ | _______________________________________^
+LL | |
+LL | |
+LL | |
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing f[(0, 0)] -> ImmBorrow
+ --> $DIR/issue-88476.rs:25:26
+ |
+LL | println!("{:?}", f.0);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/issue-88476.rs:20:39
+ |
+LL | let x = #[rustc_capture_analysis] move || {
+ | _______________________________________^
+LL | |
+LL | |
+LL | |
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture f[] -> ByValue
+ --> $DIR/issue-88476.rs:25:26
+ |
+LL | println!("{:?}", f.0);
+ | ^^^
+
+error: First Pass analysis includes:
+ --> $DIR/issue-88476.rs:47:39
+ |
+LL | let c = #[rustc_capture_analysis] move || {
+ | _______________________________________^
+LL | |
+LL | |
+LL | |
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing character[(0, 0)] -> ImmBorrow
+ --> $DIR/issue-88476.rs:52:24
+ |
+LL | println!("{}", character.hp)
+ | ^^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/issue-88476.rs:47:39
+ |
+LL | let c = #[rustc_capture_analysis] move || {
+ | _______________________________________^
+LL | |
+LL | |
+LL | |
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture character[(0, 0)] -> ByValue
+ --> $DIR/issue-88476.rs:52:24
+ |
+LL | println!("{}", character.hp)
+ | ^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-89606.rs b/src/test/ui/closures/2229_closure_analysis/issue-89606.rs
new file mode 100644
index 000000000..1bb6aa40f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-89606.rs
@@ -0,0 +1,40 @@
+// Regression test for #89606. Used to ICE.
+//
+// check-pass
+// revisions: twenty_eighteen twenty_twentyone
+// [twenty_eighteen]compile-flags: --edition 2018
+// [twenty_twentyone]compile-flags: --edition 2021
+
+struct S<'a>(Option<&'a mut i32>);
+
+fn by_ref(s: &mut S<'_>) {
+ (|| {
+ let S(_o) = s;
+ s.0 = None;
+ })();
+}
+
+fn by_value(s: S<'_>) {
+ (|| {
+ let S(ref _o) = s;
+ let _g = s.0;
+ })();
+}
+
+struct V<'a>((Option<&'a mut i32>,));
+
+fn nested(v: &mut V<'_>) {
+ (|| {
+ let V((_o,)) = v;
+ v.0 = (None, );
+ })();
+}
+
+fn main() {
+ let mut s = S(None);
+ by_ref(&mut s);
+ by_value(s);
+
+ let mut v = V((None, ));
+ nested(&mut v);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed b/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed
new file mode 100644
index 000000000..4e0b18e72
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed
@@ -0,0 +1,35 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE lint level is defined here
+
+fn main() {
+ struct Foo(u32);
+ impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("dropped {}", self.0);
+ }
+ }
+
+ let f0 = Foo(0);
+ let f1 = Foo(1);
+
+ let c0 = move || {
+ let _ = &f0;
+ //~^ ERROR changes to closure capture in Rust 2021 will affect drop order
+ //~| NOTE for more information
+ let _ = f0;
+ //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+ };
+
+ let c1 = move || {
+ let _ = &f1;
+ };
+
+ println!("dropping 0");
+ drop(c0);
+ println!("dropping 1");
+ drop(c1);
+ println!("dropped all");
+}
+//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.rs b/src/test/ui/closures/2229_closure_analysis/issue-90465.rs
new file mode 100644
index 000000000..466e6dbab
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.rs
@@ -0,0 +1,34 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE lint level is defined here
+
+fn main() {
+ struct Foo(u32);
+ impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("dropped {}", self.0);
+ }
+ }
+
+ let f0 = Foo(0);
+ let f1 = Foo(1);
+
+ let c0 = move || {
+ //~^ ERROR changes to closure capture in Rust 2021 will affect drop order
+ //~| NOTE for more information
+ let _ = f0;
+ //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+ };
+
+ let c1 = move || {
+ let _ = &f1;
+ };
+
+ println!("dropping 0");
+ drop(c0);
+ println!("dropping 1");
+ drop(c1);
+ println!("dropped all");
+}
+//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr b/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr
new file mode 100644
index 000000000..3e921dc0f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr
@@ -0,0 +1,26 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/issue-90465.rs:17:14
+ |
+LL | let c0 = move || {
+ | ^^^^^^^
+...
+LL | let _ = f0;
+ | -- in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+...
+LL | }
+ | - in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
+ |
+note: the lint level is defined here
+ --> $DIR/issue-90465.rs:3:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f0` to be fully captured
+ |
+LL ~ let c0 = move || {
+LL + let _ = &f0;
+ |
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs b/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs
new file mode 100644
index 000000000..a3b17755f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs
@@ -0,0 +1,14 @@
+// ICEs if checking if there is a significant destructor causes a query cycle
+// check-pass
+
+#![warn(rust_2021_incompatible_closure_captures)]
+pub struct Foo(Bar);
+pub struct Bar(Baz);
+pub struct Baz(Vec<Foo>);
+
+impl Foo {
+ pub fn baz(self, v: Baz) -> Baz {
+ (|| v)()
+ }
+}
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue_88118.rs b/src/test/ui/closures/2229_closure_analysis/issue_88118.rs
new file mode 100644
index 000000000..453b7e04a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/issue_88118.rs
@@ -0,0 +1,15 @@
+// Regression test for #88118. Used to ICE.
+//
+// check-pass
+
+#![allow(incomplete_features)]
+#![feature(capture_disjoint_fields)]
+
+fn foo<MsU>(handler: impl FnOnce() -> MsU + Clone + 'static) {
+ Box::new(move |value| {
+ (|_| handler.clone()())(value);
+ None
+ }) as Box<dyn Fn(i32) -> Option<i32>>;
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs b/src/test/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs
new file mode 100644
index 000000000..4060c4093
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs
@@ -0,0 +1,10 @@
+#[non_exhaustive]
+pub enum E1 {}
+
+#[non_exhaustive]
+pub enum E2 { A, B }
+
+#[non_exhaustive]
+pub enum E3 { C }
+
+pub enum E4 { D }
diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-87097.rs b/src/test/ui/closures/2229_closure_analysis/match/issue-87097.rs
new file mode 100644
index 000000000..815fc0a71
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/issue-87097.rs
@@ -0,0 +1,35 @@
+// run-pass
+// edition:2021
+
+enum Variant {
+ A,
+ B, //~ WARNING: variant `B` is never constructed
+}
+
+struct A {
+ field: Variant,
+}
+
+fn discriminant_is_a_ref() {
+ let here = A { field: Variant::A };
+ let out_ref = &here.field;
+
+ || match out_ref { //~ WARNING: unused closure that must be used
+ Variant::A => (),
+ Variant::B => (),
+ };
+}
+
+fn discriminant_is_a_field() {
+ let here = A { field: Variant::A };
+
+ || match here.field { //~ WARNING: unused closure that must be used
+ Variant::A => (),
+ Variant::B => (),
+ };
+}
+
+fn main() {
+ discriminant_is_a_ref();
+ discriminant_is_a_field();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-87097.stderr b/src/test/ui/closures/2229_closure_analysis/match/issue-87097.stderr
new file mode 100644
index 000000000..2a49ed4b5
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/issue-87097.stderr
@@ -0,0 +1,36 @@
+warning: variant `B` is never constructed
+ --> $DIR/issue-87097.rs:6:5
+ |
+LL | enum Variant {
+ | ------- variant in this enum
+LL | A,
+LL | B,
+ | ^
+ |
+ = note: `#[warn(dead_code)]` on by default
+
+warning: unused closure that must be used
+ --> $DIR/issue-87097.rs:17:5
+ |
+LL | / || match out_ref {
+LL | | Variant::A => (),
+LL | | Variant::B => (),
+LL | | };
+ | |______^
+ |
+ = note: `#[warn(unused_must_use)]` on by default
+ = note: closures are lazy and do nothing unless called
+
+warning: unused closure that must be used
+ --> $DIR/issue-87097.rs:26:5
+ |
+LL | / || match here.field {
+LL | | Variant::A => (),
+LL | | Variant::B => (),
+LL | | };
+ | |______^
+ |
+ = note: closures are lazy and do nothing unless called
+
+warning: 3 warnings emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-87426.rs b/src/test/ui/closures/2229_closure_analysis/match/issue-87426.rs
new file mode 100644
index 000000000..74506979a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/issue-87426.rs
@@ -0,0 +1,14 @@
+// run-pass
+// edition:2021
+
+pub fn foo() {
+ let ref_x_ck = 123;
+ let _y = || match ref_x_ck {
+ 2_000_000..=3_999_999 => { println!("A")}
+ _ => { println!("B")}
+ };
+}
+
+fn main() {
+ foo();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-87988.rs b/src/test/ui/closures/2229_closure_analysis/match/issue-87988.rs
new file mode 100644
index 000000000..27e7fabf1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/issue-87988.rs
@@ -0,0 +1,19 @@
+// run-pass
+// edition:2021
+
+const LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: i32 = 0x01;
+const LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: i32 = 0x02;
+
+pub fn hotplug_callback(event: i32) {
+ let _ = || {
+ match event {
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => (),
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => (),
+ _ => (),
+ };
+ };
+}
+
+fn main() {
+ hotplug_callback(1);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-88331.rs b/src/test/ui/closures/2229_closure_analysis/match/issue-88331.rs
new file mode 100644
index 000000000..0a6d71c68
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/issue-88331.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Opcode(pub u8);
+
+impl Opcode {
+ pub const OP1: Opcode = Opcode(0x1);
+}
+
+pub fn example1(msg_type: Opcode) -> impl FnMut(&[u8]) {
+ move |i| match msg_type {
+ //~^ ERROR: non-exhaustive patterns: `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+ Opcode::OP1 => unimplemented!(),
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Opcode2(Opcode);
+
+impl Opcode2 {
+ pub const OP2: Opcode2 = Opcode2(Opcode(0x1));
+}
+
+
+pub fn example2(msg_type: Opcode2) -> impl FnMut(&[u8]) {
+
+ move |i| match msg_type {
+ //~^ ERROR: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+ Opcode2::OP2=> unimplemented!(),
+ }
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/issue-88331.stderr b/src/test/ui/closures/2229_closure_analysis/match/issue-88331.stderr
new file mode 100644
index 000000000..7e22defa9
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/issue-88331.stderr
@@ -0,0 +1,39 @@
+error[E0004]: non-exhaustive patterns: `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+ --> $DIR/issue-88331.rs:11:20
+ |
+LL | move |i| match msg_type {
+ | ^^^^^^^^ patterns `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+ |
+note: `Opcode` defined here
+ --> $DIR/issue-88331.rs:4:12
+ |
+LL | pub struct Opcode(pub u8);
+ | ^^^^^^
+ = note: the matched value is of type `Opcode`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+ |
+LL ~ Opcode::OP1 => unimplemented!(),
+LL ~ Opcode(0_u8) | Opcode(2_u8..=u8::MAX) => todo!(),
+ |
+
+error[E0004]: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+ --> $DIR/issue-88331.rs:27:20
+ |
+LL | move |i| match msg_type {
+ | ^^^^^^^^ patterns `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+ |
+note: `Opcode2` defined here
+ --> $DIR/issue-88331.rs:18:12
+ |
+LL | pub struct Opcode2(Opcode);
+ | ^^^^^^^
+ = note: the matched value is of type `Opcode2`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+ |
+LL ~ Opcode2::OP2=> unimplemented!(),
+LL ~ Opcode2(Opcode(0_u8)) | Opcode2(Opcode(2_u8..=u8::MAX)) => todo!(),
+ |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs
new file mode 100644
index 000000000..914ebbe26
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs
@@ -0,0 +1,44 @@
+// run-pass
+// edition:2021
+
+const PATTERN_REF: &str = "Hello World";
+const NUMBER: i32 = 30;
+const NUMBER_POINTER: *const i32 = &NUMBER;
+
+pub fn edge_case_ref(event: &str) {
+ let _ = || {
+ match event {
+ PATTERN_REF => (),
+ _ => (),
+ };
+ };
+}
+
+pub fn edge_case_str(event: String) {
+ let _ = || {
+ match event.as_str() {
+ "hello" => (),
+ _ => (),
+ };
+ };
+}
+
+pub fn edge_case_raw_ptr(event: *const i32) {
+ let _ = || {
+ match event {
+ NUMBER_POINTER => (),
+ _ => (),
+ };
+ };
+}
+
+pub fn edge_case_char(event: char) {
+ let _ = || {
+ match event {
+ 'a' => (),
+ _ => (),
+ };
+ };
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs
new file mode 100644
index 000000000..ae724f9c3
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs
@@ -0,0 +1,37 @@
+// edition:2021
+
+enum SingleVariant {
+ A
+}
+
+struct TestStruct {
+ x: i32,
+ y: i32,
+ z: i32,
+}
+
+fn edge_case_if() {
+ let sv = SingleVariant::A;
+ let condition = true;
+ // sv should not be captured as it is a SingleVariant
+ let _a = || {
+ match sv {
+ SingleVariant::A if condition => (),
+ _ => ()
+ }
+ };
+ let mut mut_sv = sv;
+ _a();
+
+ // ts should be captured
+ let ts = TestStruct { x: 1, y: 1, z: 1 };
+ let _b = || { match ts {
+ TestStruct{ x: 1, .. } => (),
+ _ => ()
+ }};
+ let mut mut_ts = ts;
+ //~^ ERROR: cannot move out of `ts` because it is borrowed
+ _b();
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr
new file mode 100644
index 000000000..1e42d73c6
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr
@@ -0,0 +1,17 @@
+error[E0505]: cannot move out of `ts` because it is borrowed
+ --> $DIR/match-edge-cases_2.rs:32:22
+ |
+LL | let _b = || { match ts {
+ | -- -- borrow occurs due to use in closure
+ | |
+ | borrow of `ts` occurs here
+...
+LL | let mut mut_ts = ts;
+ | ^^ move out of `ts` occurs here
+LL |
+LL | _b();
+ | -- borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0505`.
diff --git a/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs b/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs
new file mode 100644
index 000000000..318673ef8
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs
@@ -0,0 +1,54 @@
+// edition:2021
+
+// aux-build:match_non_exhaustive_lib.rs
+
+/* The error message for non-exhaustive matches on non-local enums
+ * marked as non-exhaustive should mention the fact that the enum
+ * is marked as non-exhaustive (issue #85227).
+ */
+
+// Ignore non_exhaustive in the same crate
+#[non_exhaustive]
+enum L1 { A, B }
+enum L2 { C }
+
+extern crate match_non_exhaustive_lib;
+use match_non_exhaustive_lib::{E1, E2, E3, E4};
+
+fn foo() -> (L1, L2) {todo!()}
+fn bar() -> (E1, E2, E3, E4) {todo!()}
+
+fn main() {
+ let (l1, l2) = foo();
+ // No error for enums defined in this crate
+ let _a = || { match l1 { L1::A => (), L1::B => () } };
+ // (except if the match is already non-exhaustive)
+ let _b = || { match l1 { L1::A => () } };
+ //~^ ERROR: non-exhaustive patterns: `B` not covered [E0004]
+
+ // l2 should not be captured as it is a non-exhaustive SingleVariant
+ // defined in this crate
+ let _c = || { match l2 { L2::C => (), _ => () } };
+ let mut mut_l2 = l2;
+ _c();
+
+ // E1 is not visibly uninhabited from here
+ let (e1, e2, e3, e4) = bar();
+ let _d = || { match e1 {} };
+ //~^ ERROR: non-exhaustive patterns: type `E1` is non-empty [E0004]
+ let _e = || { match e2 { E2::A => (), E2::B => () } };
+ //~^ ERROR: non-exhaustive patterns: `_` not covered [E0004]
+ let _f = || { match e2 { E2::A => (), E2::B => (), _ => () } };
+
+ // e3 should be captured as it is a non-exhaustive SingleVariant
+ // defined in another crate
+ let _g = || { match e3 { E3::C => (), _ => () } };
+ let mut mut_e3 = e3;
+ //~^ ERROR: cannot move out of `e3` because it is borrowed
+ _g();
+
+ // e4 should not be captured as it is a SingleVariant
+ let _h = || { match e4 { E4::D => (), _ => () } };
+ let mut mut_e4 = e4;
+ _h();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
new file mode 100644
index 000000000..e0678bc71
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
@@ -0,0 +1,70 @@
+error[E0004]: non-exhaustive patterns: `B` not covered
+ --> $DIR/non-exhaustive-match.rs:26:25
+ |
+LL | let _b = || { match l1 { L1::A => () } };
+ | ^^ pattern `B` not covered
+ |
+note: `L1` defined here
+ --> $DIR/non-exhaustive-match.rs:12:14
+ |
+LL | enum L1 { A, B }
+ | -- ^ not covered
+ = note: the matched value is of type `L1`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+ |
+LL | let _b = || { match l1 { L1::A => (), B => todo!() } };
+ | ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: type `E1` is non-empty
+ --> $DIR/non-exhaustive-match.rs:37:25
+ |
+LL | let _d = || { match e1 {} };
+ | ^^
+ |
+note: `E1` defined here
+ --> $DIR/auxiliary/match_non_exhaustive_lib.rs:2:1
+ |
+LL | pub enum E1 {}
+ | ^^^^^^^^^^^
+ = note: the matched value is of type `E1`, which is marked as non-exhaustive
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+ |
+LL ~ let _d = || { match e1 {
+LL + _ => todo!(),
+LL ~ } };
+ |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+ --> $DIR/non-exhaustive-match.rs:39:25
+ |
+LL | let _e = || { match e2 { E2::A => (), E2::B => () } };
+ | ^^ pattern `_` not covered
+ |
+note: `E2` defined here
+ --> $DIR/auxiliary/match_non_exhaustive_lib.rs:5:1
+ |
+LL | pub enum E2 { A, B }
+ | ^^^^^^^^^^^
+ = note: the matched value is of type `E2`, which is marked as non-exhaustive
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+ |
+LL | let _e = || { match e2 { E2::A => (), E2::B => (), _ => todo!() } };
+ | ++++++++++++++
+
+error[E0505]: cannot move out of `e3` because it is borrowed
+ --> $DIR/non-exhaustive-match.rs:46:22
+ |
+LL | let _g = || { match e3 { E3::C => (), _ => () } };
+ | -- -- borrow occurs due to use in closure
+ | |
+ | borrow of `e3` occurs here
+LL | let mut mut_e3 = e3;
+ | ^^ move out of `e3` occurs here
+LL |
+LL | _g();
+ | -- borrow later used here
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0004, E0505.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs b/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs
new file mode 100644
index 000000000..69cf920de
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs
@@ -0,0 +1,79 @@
+// edition:2021
+
+#![feature(never_type)]
+
+// Should fake read the discriminant and throw an error
+fn test1() {
+ let x: !;
+ let c1 = || match x { };
+ //~^ ERROR E0381
+}
+
+// Should fake read the discriminant and throw an error
+fn test2() {
+ let x: !;
+ let c2 = || match x { _ => () };
+ //~^ ERROR E0381
+}
+
+// Testing single variant patterns
+enum SingleVariant {
+ Points(u32)
+}
+
+// Should fake read the discriminant and throw an error
+fn test3() {
+ let variant: !;
+ let c = || {
+ //~^ ERROR E0381
+ match variant {
+ SingleVariant::Points(_) => {}
+ }
+ };
+ c();
+}
+
+// Should fake read the discriminant and throw an error
+fn test4() {
+ let variant: !;
+ let c = || { //~ ERROR E0381
+ match variant {
+ SingleVariant::Points(a) => {
+ println!("{:?}", a);
+ }
+ }
+ };
+ c();
+}
+
+fn test5() {
+ let t: !;
+ let g: !;
+
+ let a = || {
+ match g { }; //~ ERROR E0381
+ let c = || {
+ match t { }; //~ ERROR E0381
+ };
+
+ c();
+ };
+
+}
+
+// Should fake read the discriminant and throw an error
+fn test6() {
+ let x: u8;
+ let c1 = || match x { };
+ //~^ ERROR E0381
+ //~| ERROR: non-exhaustive patterns: type `u8` is non-empty
+}
+
+fn main() {
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr b/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr
new file mode 100644
index 000000000..fea5441ec
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr
@@ -0,0 +1,83 @@
+error[E0004]: non-exhaustive patterns: type `u8` is non-empty
+ --> $DIR/pattern-matching-should-fail.rs:67:23
+ |
+LL | let c1 = || match x { };
+ | ^
+ |
+ = note: the matched value is of type `u8`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+ |
+LL ~ let c1 = || match x {
+LL + _ => todo!(),
+LL ~ };
+ |
+
+error[E0381]: used binding `x` isn't initialized
+ --> $DIR/pattern-matching-should-fail.rs:8:23
+ |
+LL | let x: !;
+ | - binding declared here but left uninitialized
+LL | let c1 = || match x { };
+ | ^ `x` used here but it isn't initialized
+
+error[E0381]: used binding `x` isn't initialized
+ --> $DIR/pattern-matching-should-fail.rs:15:14
+ |
+LL | let x: !;
+ | - binding declared here but left uninitialized
+LL | let c2 = || match x { _ => () };
+ | ^^ - borrow occurs due to use in closure
+ | |
+ | `x` used here but it isn't initialized
+
+error[E0381]: used binding `variant` isn't initialized
+ --> $DIR/pattern-matching-should-fail.rs:27:13
+ |
+LL | let variant: !;
+ | ------- binding declared here but left uninitialized
+LL | let c = || {
+ | ^^ `variant` used here but it isn't initialized
+LL |
+LL | match variant {
+ | ------- borrow occurs due to use in closure
+
+error[E0381]: used binding `variant` isn't initialized
+ --> $DIR/pattern-matching-should-fail.rs:39:13
+ |
+LL | let variant: !;
+ | ------- binding declared here but left uninitialized
+LL | let c = || {
+ | ^^ `variant` used here but it isn't initialized
+LL | match variant {
+ | ------- borrow occurs due to use in closure
+
+error[E0381]: used binding `g` isn't initialized
+ --> $DIR/pattern-matching-should-fail.rs:54:15
+ |
+LL | let g: !;
+ | - binding declared here but left uninitialized
+...
+LL | match g { };
+ | ^ `g` used here but it isn't initialized
+
+error[E0381]: used binding `t` isn't initialized
+ --> $DIR/pattern-matching-should-fail.rs:56:19
+ |
+LL | let t: !;
+ | - binding declared here but left uninitialized
+...
+LL | match t { };
+ | ^ `t` used here but it isn't initialized
+
+error[E0381]: used binding `x` isn't initialized
+ --> $DIR/pattern-matching-should-fail.rs:67:23
+ |
+LL | let x: u8;
+ | - binding declared here but left uninitialized
+LL | let c1 = || match x { };
+ | ^ `x` used here but it isn't initialized
+
+error: aborting due to 8 previous errors
+
+Some errors have detailed explanations: E0004, E0381.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs b/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs
new file mode 100644
index 000000000..56f5ac44d
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs
@@ -0,0 +1,137 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Should capture the discriminant since a variant of a multivariant enum is
+// mentioned in the match arm; the discriminant is captured by the closure regardless
+// of if it creates a binding
+fn test_1_should_capture() {
+ let variant = Some(2229);
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ match variant {
+ //~^ NOTE: Capturing variant[] -> ImmBorrow
+ //~| NOTE: Min Capture variant[] -> ImmBorrow
+ Some(_) => {}
+ _ => {}
+ }
+ };
+ c();
+}
+
+// Should not capture the discriminant since only a wildcard is mentioned in the
+// match arm
+fn test_2_should_not_capture() {
+ let variant = Some(2229);
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ match variant {
+ _ => {}
+ }
+ };
+ c();
+}
+
+// Testing single variant patterns
+enum SingleVariant {
+ Points(u32)
+}
+
+// Should not capture the discriminant since the single variant mentioned
+// in the match arm does not trigger a binding
+fn test_3_should_not_capture_single_variant() {
+ let variant = SingleVariant::Points(1);
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ match variant {
+ SingleVariant::Points(_) => {}
+ }
+ };
+ c();
+}
+
+// Should not capture the discriminant since the single variant mentioned
+// in the match arm does not trigger a binding
+fn test_6_should_capture_single_variant() {
+ let variant = SingleVariant::Points(1);
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ match variant {
+ //~^ NOTE: Capturing variant[] -> ImmBorrow
+ //~| NOTE: Capturing variant[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture variant[] -> ImmBorrow
+ SingleVariant::Points(a) => {
+ println!("{:?}", a);
+ }
+ }
+ };
+ c();
+}
+
+// Should not capture the discriminant since only wildcards are mentioned in the
+// match arm
+fn test_4_should_not_capture_array() {
+ let array: [i32; 3] = [0; 3];
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ match array {
+ [_,_,_] => {}
+ }
+ };
+ c();
+}
+
+// Testing MultiVariant patterns
+enum MVariant {
+ A,
+ B,
+ C,
+}
+
+// Should capture the discriminant since a variant of the multi variant enum is
+// mentioned in the match arm; the discriminant is captured by the closure
+// regardless of if it creates a binding
+fn test_5_should_capture_multi_variant() {
+ let variant = MVariant::A;
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ First Pass analysis includes:
+ //~| Min Capture analysis includes:
+ match variant {
+ //~^ NOTE: Capturing variant[] -> ImmBorrow
+ //~| NOTE: Min Capture variant[] -> ImmBorrow
+ MVariant::A => {}
+ _ => {}
+ }
+ };
+ c();
+}
+
+fn main() {
+ test_1_should_capture();
+ test_2_should_not_capture();
+ test_3_should_not_capture_single_variant();
+ test_6_should_capture_single_variant();
+ test_4_should_not_capture_array();
+ test_5_should_capture_multi_variant();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr b/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr
new file mode 100644
index 000000000..460813333
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr
@@ -0,0 +1,203 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/patterns-capture-analysis.rs:10:14
+ |
+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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/patterns-capture-analysis.rs:31:14
+ |
+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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/patterns-capture-analysis.rs:52:14
+ |
+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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/patterns-capture-analysis.rs:68:14
+ |
+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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/patterns-capture-analysis.rs:90:14
+ |
+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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/patterns-capture-analysis.rs:114:14
+ |
+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
+
+error: First Pass analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:14:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | match variant {
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+note: Capturing variant[] -> ImmBorrow
+ --> $DIR/patterns-capture-analysis.rs:17:15
+ |
+LL | match variant {
+ | ^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:14:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | match variant {
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+note: Min Capture variant[] -> ImmBorrow
+ --> $DIR/patterns-capture-analysis.rs:17:15
+ |
+LL | match variant {
+ | ^^^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:34:5
+ |
+LL | / || {
+LL | |
+LL | | match variant {
+LL | | _ => {}
+LL | | }
+LL | | };
+ | |_____^
+
+error: First Pass analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:55:5
+ |
+LL | / || {
+LL | |
+LL | | match variant {
+LL | | SingleVariant::Points(_) => {}
+LL | | }
+LL | | };
+ | |_____^
+
+error: First Pass analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:71:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | match variant {
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+note: Capturing variant[] -> ImmBorrow
+ --> $DIR/patterns-capture-analysis.rs:74:15
+ |
+LL | match variant {
+ | ^^^^^^^
+note: Capturing variant[(0, 0)] -> ImmBorrow
+ --> $DIR/patterns-capture-analysis.rs:74:15
+ |
+LL | match variant {
+ | ^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:71:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | match variant {
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+note: Min Capture variant[] -> ImmBorrow
+ --> $DIR/patterns-capture-analysis.rs:74:15
+ |
+LL | match variant {
+ | ^^^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:93:5
+ |
+LL | / || {
+LL | |
+LL | | match array {
+LL | | [_,_,_] => {}
+LL | | }
+LL | | };
+ | |_____^
+
+error: First Pass analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:117:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | match variant {
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+note: Capturing variant[] -> ImmBorrow
+ --> $DIR/patterns-capture-analysis.rs:120:15
+ |
+LL | match variant {
+ | ^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/patterns-capture-analysis.rs:117:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | match variant {
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+note: Min Capture variant[] -> ImmBorrow
+ --> $DIR/patterns-capture-analysis.rs:120:15
+ |
+LL | match variant {
+ | ^^^^^^^
+
+error: aborting due to 15 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed
new file mode 100644
index 000000000..26703fbf8
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed
@@ -0,0 +1,88 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+use std::thread;
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+/* Test Send Trait Migration */
+struct SendPointer(*mut i32);
+unsafe impl Send for SendPointer {}
+
+fn test_send_trait() {
+ let mut f = 10;
+ let fptr = SendPointer(&mut f as *mut i32);
+ thread::spawn(move || { let _ = &fptr; unsafe {
+ //~^ ERROR: changes to closure capture
+ //~| NOTE: in Rust 2018, this closure implements `Send`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `fptr` to be fully captured
+ *fptr.0 = 20;
+ //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
+ } });
+}
+
+/* Test Sync Trait Migration */
+struct CustomInt(*mut i32);
+struct SyncPointer(CustomInt);
+unsafe impl Sync for SyncPointer {}
+unsafe impl Send for CustomInt {}
+
+fn test_sync_trait() {
+ let mut f = 10;
+ let f = CustomInt(&mut f as *mut i32);
+ let fptr = SyncPointer(f);
+ thread::spawn(move || { let _ = &fptr; unsafe {
+ //~^ ERROR: changes to closure capture
+ //~| NOTE: in Rust 2018, this closure implements `Sync`
+ //~| NOTE: in Rust 2018, this closure implements `Send`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `fptr` to be fully captured
+ *fptr.0.0 = 20;
+ //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
+ } });
+}
+
+/* Test Clone Trait Migration */
+struct S(Foo);
+struct T(i32);
+
+struct U(S, T);
+
+impl Clone for U {
+ fn clone(&self) -> Self {
+ U(S(Foo(0)), T(0))
+ }
+}
+
+fn test_clone_trait() {
+ let f = U(S(Foo(0)), T(0));
+ let c = || {
+ let _ = &f;
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+ //~| NOTE: in Rust 2018, this closure implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f` to be fully captured
+ let f_1 = f.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1`
+ println!("{:?}", f_1.0);
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+//~^ NOTE: in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure
+
+fn main() {
+ test_send_trait();
+ test_sync_trait();
+ test_clone_trait();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs
new file mode 100644
index 000000000..932db51d4
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs
@@ -0,0 +1,87 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+use std::thread;
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+/* Test Send Trait Migration */
+struct SendPointer(*mut i32);
+unsafe impl Send for SendPointer {}
+
+fn test_send_trait() {
+ let mut f = 10;
+ let fptr = SendPointer(&mut f as *mut i32);
+ thread::spawn(move || unsafe {
+ //~^ ERROR: changes to closure capture
+ //~| NOTE: in Rust 2018, this closure implements `Send`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `fptr` to be fully captured
+ *fptr.0 = 20;
+ //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
+ });
+}
+
+/* Test Sync Trait Migration */
+struct CustomInt(*mut i32);
+struct SyncPointer(CustomInt);
+unsafe impl Sync for SyncPointer {}
+unsafe impl Send for CustomInt {}
+
+fn test_sync_trait() {
+ let mut f = 10;
+ let f = CustomInt(&mut f as *mut i32);
+ let fptr = SyncPointer(f);
+ thread::spawn(move || unsafe {
+ //~^ ERROR: changes to closure capture
+ //~| NOTE: in Rust 2018, this closure implements `Sync`
+ //~| NOTE: in Rust 2018, this closure implements `Send`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `fptr` to be fully captured
+ *fptr.0.0 = 20;
+ //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
+ });
+}
+
+/* Test Clone Trait Migration */
+struct S(Foo);
+struct T(i32);
+
+struct U(S, T);
+
+impl Clone for U {
+ fn clone(&self) -> Self {
+ U(S(Foo(0)), T(0))
+ }
+}
+
+fn test_clone_trait() {
+ let f = U(S(Foo(0)), T(0));
+ let c = || {
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+ //~| NOTE: in Rust 2018, this closure implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f` to be fully captured
+ let f_1 = f.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1`
+ println!("{:?}", f_1.0);
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+//~^ NOTE: in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure
+
+fn main() {
+ test_send_trait();
+ test_sync_trait();
+ test_clone_trait();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr
new file mode 100644
index 000000000..d7104bafe
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr
@@ -0,0 +1,67 @@
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+ --> $DIR/auto_traits.rs:22:19
+ |
+LL | thread::spawn(move || unsafe {
+ | ^^^^^^^ in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0` does not implement `Send`
+...
+LL | *fptr.0 = 20;
+ | ------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
+ |
+note: the lint level is defined here
+ --> $DIR/auto_traits.rs:2:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `fptr` to be fully captured
+ |
+LL ~ thread::spawn(move || { let _ = &fptr; unsafe {
+LL |
+ ...
+LL |
+LL ~ } });
+ |
+
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+ --> $DIR/auto_traits.rs:42:19
+ |
+LL | thread::spawn(move || unsafe {
+ | ^^^^^^^
+ | |
+ | in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0.0` does not implement `Send`
+ | in Rust 2018, this closure implements `Sync` as `fptr` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr` is not fully captured and `fptr.0.0` does not implement `Sync`
+...
+LL | *fptr.0.0 = 20;
+ | --------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `fptr` to be fully captured
+ |
+LL ~ thread::spawn(move || { let _ = &fptr; unsafe {
+LL |
+ ...
+LL |
+LL ~ } });
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+ --> $DIR/auto_traits.rs:67:13
+ |
+LL | let c = || {
+ | ^^ in Rust 2018, this closure implements `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f` is not fully captured and `f.1` does not implement `Clone`
+...
+LL | let f_1 = f.1;
+ | --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1`
+...
+LL | }
+ | - in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &f;
+ |
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed
new file mode 100644
index 000000000..9a6db588c
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed
@@ -0,0 +1,33 @@
+// run-rustfix
+// edition:2018
+// check-pass
+#![warn(rust_2021_compatibility)]
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+macro_rules! m {
+ (@ $body:expr) => {{
+ let f = || $body;
+ //~^ WARNING: drop order
+ f();
+ }};
+ ($body:block) => {{
+ m!(@ $body);
+ }};
+}
+
+fn main() {
+ let a = (Foo(0), Foo(1));
+ m!({
+ let _ = &a;
+ //~^ HELP: add a dummy
+ let x = a.0;
+ println!("{:?}", x);
+ });
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs
new file mode 100644
index 000000000..08cc24b4b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs
@@ -0,0 +1,32 @@
+// run-rustfix
+// edition:2018
+// check-pass
+#![warn(rust_2021_compatibility)]
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+macro_rules! m {
+ (@ $body:expr) => {{
+ let f = || $body;
+ //~^ WARNING: drop order
+ f();
+ }};
+ ($body:block) => {{
+ m!(@ $body);
+ }};
+}
+
+fn main() {
+ let a = (Foo(0), Foo(1));
+ m!({
+ //~^ HELP: add a dummy
+ let x = a.0;
+ println!("{:?}", x);
+ });
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr
new file mode 100644
index 000000000..c611daf13
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr
@@ -0,0 +1,33 @@
+warning: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/closure-body-macro-fragment.rs:16:17
+ |
+LL | let f = || $body;
+ | ^^
+...
+LL | }};
+ | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure
+...
+LL | / m!({
+LL | |
+LL | | let x = a.0;
+ | | --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0`
+LL | | println!("{:?}", x);
+LL | | });
+ | |______- in this macro invocation
+ |
+note: the lint level is defined here
+ --> $DIR/closure-body-macro-fragment.rs:4:9
+ |
+LL | #![warn(rust_2021_compatibility)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ = note: `#[warn(rust_2021_incompatible_closure_captures)]` implied by `#[warn(rust_2021_compatibility)]`
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+ = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: add a dummy let to cause `a` to be fully captured
+ |
+LL ~ m!({
+LL + let _ = &a;
+ |
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed
new file mode 100644
index 000000000..2652bf598
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed
@@ -0,0 +1,38 @@
+// run-pass
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+#![allow(unused)]
+
+// Test cases for types that implement an insignificant drop (stlib defined)
+
+macro_rules! test_insig_dtor_for_type {
+ ($t: ty, $disambiguator: ident) => {
+ mod $disambiguator {
+ use std::collections::*;
+ use std::rc::Rc;
+ use std::sync::Mutex;
+
+ fn test_for_type(t: $t) {
+ let tup = (Mutex::new(0), t);
+
+ let _c = || tup.0;
+ }
+ }
+ };
+}
+
+test_insig_dtor_for_type!(i32, prim_i32);
+test_insig_dtor_for_type!(Vec<i32>, vec_i32);
+test_insig_dtor_for_type!(String, string);
+test_insig_dtor_for_type!(Vec<String>, vec_string);
+test_insig_dtor_for_type!(HashMap<String, String>, hash_map);
+test_insig_dtor_for_type!(BTreeMap<String, i32>, btree_map);
+test_insig_dtor_for_type!(LinkedList<String>, linked_list);
+test_insig_dtor_for_type!(Rc<i32>, rc_i32);
+test_insig_dtor_for_type!(Rc<String>, rc_string);
+test_insig_dtor_for_type!(std::vec::IntoIter<String>, vec_into_iter);
+test_insig_dtor_for_type!(btree_map::IntoIter<String, String>, btree_map_into_iter);
+test_insig_dtor_for_type!(std::array::IntoIter<String, 5>, array_into_iter);
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
new file mode 100644
index 000000000..2652bf598
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
@@ -0,0 +1,38 @@
+// run-pass
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+#![allow(unused)]
+
+// Test cases for types that implement an insignificant drop (stlib defined)
+
+macro_rules! test_insig_dtor_for_type {
+ ($t: ty, $disambiguator: ident) => {
+ mod $disambiguator {
+ use std::collections::*;
+ use std::rc::Rc;
+ use std::sync::Mutex;
+
+ fn test_for_type(t: $t) {
+ let tup = (Mutex::new(0), t);
+
+ let _c = || tup.0;
+ }
+ }
+ };
+}
+
+test_insig_dtor_for_type!(i32, prim_i32);
+test_insig_dtor_for_type!(Vec<i32>, vec_i32);
+test_insig_dtor_for_type!(String, string);
+test_insig_dtor_for_type!(Vec<String>, vec_string);
+test_insig_dtor_for_type!(HashMap<String, String>, hash_map);
+test_insig_dtor_for_type!(BTreeMap<String, i32>, btree_map);
+test_insig_dtor_for_type!(LinkedList<String>, linked_list);
+test_insig_dtor_for_type!(Rc<i32>, rc_i32);
+test_insig_dtor_for_type!(Rc<String>, rc_string);
+test_insig_dtor_for_type!(std::vec::IntoIter<String>, vec_into_iter);
+test_insig_dtor_for_type!(btree_map::IntoIter<String, String>, btree_map_into_iter);
+test_insig_dtor_for_type!(std::array::IntoIter<String, 5>, array_into_iter);
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed
new file mode 100644
index 000000000..d985e3bb9
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed
@@ -0,0 +1,76 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+use std::sync::Mutex;
+
+ #[rustc_insignificant_dtor]
+struct InsignificantDropPoint {
+ x: i32,
+ y: Mutex<i32>,
+}
+
+impl Drop for InsignificantDropPoint {
+ fn drop(&mut self) {}
+}
+
+struct SigDrop;
+
+impl Drop for SigDrop {
+ fn drop(&mut self) {}
+}
+
+#[rustc_insignificant_dtor]
+struct GenericStruct<T>(T, T);
+
+impl<T> Drop for GenericStruct<T> {
+ fn drop(&mut self) {}
+}
+
+struct Wrapper<T>(GenericStruct<T>, i32);
+
+// `SigDrop` implements drop and therefore needs to be migrated.
+fn significant_drop_needs_migration() {
+ let t = (SigDrop {}, SigDrop {});
+
+ let c = || {
+ let _ = &t;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Even if a type implements an insignificant drop, if it's
+// elements have a significant drop then the overall type is
+// consdered to have an significant drop. Since the elements
+// of `GenericStruct` implement drop, migration is required.
+fn generic_struct_with_significant_drop_needs_migration() {
+ let t = Wrapper(GenericStruct(SigDrop {}, SigDrop {}), 5);
+
+ // move is used to force i32 to be copied instead of being a ref
+ let c = move || {
+ let _ = &t;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+
+fn main() {
+ significant_drop_needs_migration();
+ generic_struct_with_significant_drop_needs_migration();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs
new file mode 100644
index 000000000..f95d34eeb
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs
@@ -0,0 +1,74 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+use std::sync::Mutex;
+
+ #[rustc_insignificant_dtor]
+struct InsignificantDropPoint {
+ x: i32,
+ y: Mutex<i32>,
+}
+
+impl Drop for InsignificantDropPoint {
+ fn drop(&mut self) {}
+}
+
+struct SigDrop;
+
+impl Drop for SigDrop {
+ fn drop(&mut self) {}
+}
+
+#[rustc_insignificant_dtor]
+struct GenericStruct<T>(T, T);
+
+impl<T> Drop for GenericStruct<T> {
+ fn drop(&mut self) {}
+}
+
+struct Wrapper<T>(GenericStruct<T>, i32);
+
+// `SigDrop` implements drop and therefore needs to be migrated.
+fn significant_drop_needs_migration() {
+ let t = (SigDrop {}, SigDrop {});
+
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Even if a type implements an insignificant drop, if it's
+// elements have a significant drop then the overall type is
+// consdered to have an significant drop. Since the elements
+// of `GenericStruct` implement drop, migration is required.
+fn generic_struct_with_significant_drop_needs_migration() {
+ let t = Wrapper(GenericStruct(SigDrop {}, SigDrop {}), 5);
+
+ // move is used to force i32 to be copied instead of being a ref
+ let c = move || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+
+fn main() {
+ significant_drop_needs_migration();
+ generic_struct_with_significant_drop_needs_migration();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr
new file mode 100644
index 000000000..832a81711
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr
@@ -0,0 +1,45 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/insignificant_drop_attr_migrations.rs:39:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.0;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ |
+note: the lint level is defined here
+ --> $DIR/insignificant_drop_attr_migrations.rs:3:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &t;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/insignificant_drop_attr_migrations.rs:59:13
+ |
+LL | let c = move || {
+ | ^^^^^^^
+...
+LL | let _t = t.1;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL ~ let c = move || {
+LL + let _ = &t;
+ |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs
new file mode 100644
index 000000000..3f184a67f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs
@@ -0,0 +1,45 @@
+// run-pass
+
+#![deny(rust_2021_incompatible_closure_captures)]
+#![feature(rustc_attrs)]
+#![allow(unused)]
+#[rustc_insignificant_dtor]
+
+struct InsignificantDropPoint {
+ x: i32,
+ y: i32,
+}
+
+impl Drop for InsignificantDropPoint {
+ fn drop(&mut self) {}
+}
+
+struct GenericStruct<T>(T, T);
+
+// No drop reordering is required as the elements of `t` implement insignificant drop
+fn insignificant_drop_does_not_need_migration() {
+ let t = (InsignificantDropPoint { x: 4, y: 9 }, InsignificantDropPoint { x: 4, y: 9 });
+
+ let c = || {
+ let _t = t.0;
+ };
+
+ c();
+}
+
+// Generic struct whose elements don't have significant drops don't need drop reordering
+fn generic_struct_with_insignificant_drop_does_not_need_migration() {
+ let t =
+ GenericStruct(InsignificantDropPoint { x: 4, y: 9 }, InsignificantDropPoint { x: 4, y: 9 });
+
+ let c = || {
+ let _t = t.0;
+ };
+
+ c();
+}
+
+fn main() {
+ insignificant_drop_does_not_need_migration();
+ generic_struct_with_insignificant_drop_does_not_need_migration();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs
new file mode 100644
index 000000000..ff5d28461
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs
@@ -0,0 +1,10 @@
+// run-pass
+
+#![warn(rust_2021_incompatible_closure_captures)]
+
+fn main() {
+ if let a = "" {
+ //~^ WARNING: irrefutable `if let` pattern
+ drop(|_: ()| drop(a));
+ }
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr
new file mode 100644
index 000000000..41b675f79
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr
@@ -0,0 +1,12 @@
+warning: irrefutable `if let` pattern
+ --> $DIR/issue-78720.rs:6:8
+ |
+LL | if let a = "" {
+ | ^^^^^^^^^^
+ |
+ = note: `#[warn(irrefutable_let_patterns)]` on by default
+ = note: this pattern will always match, so the `if let` is useless
+ = help: consider replacing the `if let` with a `let`
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs
new file mode 100644
index 000000000..fce9cac62
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs
@@ -0,0 +1,34 @@
+// edition:2018
+// check-pass
+
+#![warn(rust_2021_compatibility)]
+
+use std::future::Future;
+
+struct Runtime;
+
+impl Runtime {
+ pub fn block_on<F: Future>(&self, _future: F) -> F::Output {
+ unimplemented!()
+ }
+}
+
+pub fn http<F, Fut>(_func: F)
+where
+ F: Fn() -> Fut,
+ Fut: Future<Output = ()>,
+{
+ let rt = Runtime {};
+ let srv = rt.block_on(async move { serve(move || async move { unimplemented!() }) });
+ let _ = || rt.block_on(async { srv });
+}
+
+pub struct Server<S> {
+ _marker: std::marker::PhantomData<S>,
+}
+
+pub fn serve<S>(_new_service: S) -> Server<S> {
+ unimplemented!()
+}
+
+fn main() { }
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs
new file mode 100644
index 000000000..ed8cb042b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs
@@ -0,0 +1,37 @@
+// Test that rustc doesn't ICE as in #90024.
+// check-pass
+// edition=2018
+
+#![warn(rust_2021_incompatible_closure_captures)]
+
+// Checks there's no double-subst into the generic args, otherwise we get OOB
+// MCVE by @lqd
+pub struct Graph<N, E, Ix> {
+ _edges: E,
+ _nodes: N,
+ _ix: Vec<Ix>,
+}
+fn graph<N, E>() -> Graph<N, E, i32> {
+ todo!()
+}
+fn first_ice() {
+ let g = graph::<i32, i32>();
+ let _ = || g;
+}
+
+// Checks that there is a subst into the fields, otherwise we get normalization error
+// MCVE by @cuviper
+use std::iter::Empty;
+struct Foo<I: Iterator> {
+ data: Vec<I::Item>,
+}
+pub fn second_ice() {
+ let v = Foo::<Empty<()>> { data: vec![] };
+
+ (|| v.data[0])();
+}
+
+pub fn main() {
+ first_ice();
+ second_ice();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed
new file mode 100644
index 000000000..31fe494dc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+
+// See https://github.com/rust-lang/rust/issues/87955
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+fn main() {
+ let a = (Foo(0), Foo(1));
+ let _ = || { let _ = &a; dbg!(a.0) };
+ //~^ ERROR: drop order
+ //~| NOTE: will only capture `a.0`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `a` to be fully captured
+}
+//~^ NOTE: dropped here
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs b/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs
new file mode 100644
index 000000000..0f0c49749
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.rs
@@ -0,0 +1,25 @@
+// run-rustfix
+
+// See https://github.com/rust-lang/rust/issues/87955
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+fn main() {
+ let a = (Foo(0), Foo(1));
+ let _ = || dbg!(a.0);
+ //~^ ERROR: drop order
+ //~| NOTE: will only capture `a.0`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `a` to be fully captured
+}
+//~^ NOTE: dropped here
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr
new file mode 100644
index 000000000..2d0c56aad
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/macro.stderr
@@ -0,0 +1,22 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/macro.rs:19:13
+ |
+LL | let _ = || dbg!(a.0);
+ | ^^ --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0`
+...
+LL | }
+ | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure
+ |
+note: the lint level is defined here
+ --> $DIR/macro.rs:5:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `a` to be fully captured
+ |
+LL | let _ = || { let _ = &a; dbg!(a.0) };
+ | +++++++++++++ +
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed
new file mode 100644
index 000000000..ce8b60725
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed
@@ -0,0 +1,47 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+// Test the two possible cases for automated migartion using rustfix
+// - Closure contains a block i.e. `|| { .. };`
+// - Closure contains just an expr `|| ..;`
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+fn closure_contains_block() {
+ let t = (Foo(0), Foo(0));
+ let c = || {
+ let _ = &t;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+fn closure_doesnt_contain_block() {
+ let t = (Foo(0), Foo(0));
+ let c = || { let _ = &t; t.0 };
+ //~^ ERROR: drop order
+ //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+fn main() {
+ closure_contains_block();
+ closure_doesnt_contain_block();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs
new file mode 100644
index 000000000..2237bebd7
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs
@@ -0,0 +1,46 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+// Test the two possible cases for automated migartion using rustfix
+// - Closure contains a block i.e. `|| { .. };`
+// - Closure contains just an expr `|| ..;`
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+fn closure_contains_block() {
+ let t = (Foo(0), Foo(0));
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+fn closure_doesnt_contain_block() {
+ let t = (Foo(0), Foo(0));
+ let c = || t.0;
+ //~^ ERROR: drop order
+ //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+fn main() {
+ closure_contains_block();
+ closure_doesnt_contain_block();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr
new file mode 100644
index 000000000..12760cc72
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr
@@ -0,0 +1,41 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/migrations_rustfix.rs:19:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.0;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ |
+note: the lint level is defined here
+ --> $DIR/migrations_rustfix.rs:2:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &t;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/migrations_rustfix.rs:33:13
+ |
+LL | let c = || t.0;
+ | ^^ --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL | let c = || { let _ = &t; t.0 };
+ | +++++++++++++ +
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed
new file mode 100644
index 000000000..89f393141
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed
@@ -0,0 +1,51 @@
+// run-rustfix
+// needs-unwind
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+// ignore-wasm32-bare compiled with panic=abort by default
+#![feature(fn_traits)]
+#![feature(never_type)]
+
+use std::panic;
+
+fn foo_diverges() -> ! {
+ panic!()
+}
+
+fn assert_panics<F>(f: F)
+where
+ F: FnOnce(),
+{
+ let f = panic::AssertUnwindSafe(f);
+ let result = panic::catch_unwind(move || {
+ let _ = &f;
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+ //~| NOTE: in Rust 2018, this closure implements `UnwindSafe`
+ //~| NOTE: in Rust 2018, this closure implements `RefUnwindSafe`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f` to be fully captured
+ f.0()
+ //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`
+ });
+ if let Ok(..) = result {
+ panic!("diverging function returned");
+ }
+}
+
+fn test_fn_ptr_panic<T>(mut t: T)
+where
+ T: Fn() -> !,
+{
+ let as_fn = <T as Fn<()>>::call;
+ assert_panics(|| as_fn(&t, ()));
+ let as_fn_mut = <T as FnMut<()>>::call_mut;
+ assert_panics(|| as_fn_mut(&mut t, ()));
+ let as_fn_once = <T as FnOnce<()>>::call_once;
+ assert_panics(|| as_fn_once(t, ()));
+}
+
+fn main() {
+ test_fn_ptr_panic(foo_diverges);
+ test_fn_ptr_panic(foo_diverges as fn() -> !);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs
new file mode 100644
index 000000000..6b0b10521
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs
@@ -0,0 +1,50 @@
+// run-rustfix
+// needs-unwind
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+// ignore-wasm32-bare compiled with panic=abort by default
+#![feature(fn_traits)]
+#![feature(never_type)]
+
+use std::panic;
+
+fn foo_diverges() -> ! {
+ panic!()
+}
+
+fn assert_panics<F>(f: F)
+where
+ F: FnOnce(),
+{
+ let f = panic::AssertUnwindSafe(f);
+ let result = panic::catch_unwind(move || {
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+ //~| NOTE: in Rust 2018, this closure implements `UnwindSafe`
+ //~| NOTE: in Rust 2018, this closure implements `RefUnwindSafe`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f` to be fully captured
+ f.0()
+ //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`
+ });
+ if let Ok(..) = result {
+ panic!("diverging function returned");
+ }
+}
+
+fn test_fn_ptr_panic<T>(mut t: T)
+where
+ T: Fn() -> !,
+{
+ let as_fn = <T as Fn<()>>::call;
+ assert_panics(|| as_fn(&t, ()));
+ let as_fn_mut = <T as FnMut<()>>::call_mut;
+ assert_panics(|| as_fn_mut(&mut t, ()));
+ let as_fn_once = <T as FnOnce<()>>::call_once;
+ assert_panics(|| as_fn_once(t, ()));
+}
+
+fn main() {
+ test_fn_ptr_panic(foo_diverges);
+ test_fn_ptr_panic(foo_diverges as fn() -> !);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr
new file mode 100644
index 000000000..2648b0043
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr
@@ -0,0 +1,26 @@
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+ --> $DIR/mir_calls_to_shims.rs:21:38
+ |
+LL | let result = panic::catch_unwind(move || {
+ | ^^^^^^^
+ | |
+ | in Rust 2018, this closure implements `RefUnwindSafe` as `f` implements `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `RefUnwindSafe` because `f` is not fully captured and `f.0` does not implement `RefUnwindSafe`
+ | in Rust 2018, this closure implements `UnwindSafe` as `f` implements `UnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe` because `f` is not fully captured and `f.0` does not implement `UnwindSafe`
+...
+LL | f.0()
+ | --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`
+ |
+note: the lint level is defined here
+ --> $DIR/mir_calls_to_shims.rs:4:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f` to be fully captured
+ |
+LL ~ let result = panic::catch_unwind(move || {
+LL + let _ = &f;
+ |
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed
new file mode 100644
index 000000000..173dd2e2c
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed
@@ -0,0 +1,157 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+use std::thread;
+
+#[derive(Debug)]
+struct Foo(String);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+impl Foo {
+ fn from(s: &str) -> Self {
+ Self(String::from(s))
+ }
+}
+
+struct S(#[allow(unused_tuple_struct_fields)] Foo);
+
+#[derive(Clone)]
+struct T(#[allow(unused_tuple_struct_fields)] i32);
+
+struct U(S, T);
+
+impl Clone for U {
+ fn clone(&self) -> Self {
+ U(S(Foo::from("Hello World")), T(0))
+ }
+}
+
+fn test_multi_issues() {
+ let f1 = U(S(Foo::from("foo")), T(0));
+ let f2 = U(S(Foo::from("bar")), T(0));
+ let c = || {
+ let _ = (&f1, &f2);
+ //~^ ERROR: changes to closure capture in Rust 2021
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured
+ let _f_1 = f1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ let _f_2 = f2.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1`
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+//~^ NOTE: in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure
+
+fn test_capturing_all_disjoint_fields_individually() {
+ let f1 = U(S(Foo::from("foo")), T(0));
+ let c = || {
+ let _ = &f1;
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f1` to be fully captured
+ let _f_1 = f1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ let _f_2 = f1.1;
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+
+struct U1(S, T, S);
+
+impl Clone for U1 {
+ fn clone(&self) -> Self {
+ U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")))
+ }
+}
+
+fn test_capturing_several_disjoint_fields_individually_1() {
+ let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
+ let c = || {
+ let _ = &f1;
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f1` to be fully captured
+ let _f_0 = f1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ let _f_2 = f1.2;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2`
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+
+fn test_capturing_several_disjoint_fields_individually_2() {
+ let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
+ let c = || {
+ let _ = &f1;
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f1` to be fully captured
+ let _f_0 = f1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ let _f_1 = f1.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1`
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+//~^ NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure
+
+struct SendPointer(*mut i32);
+unsafe impl Send for SendPointer {}
+
+struct CustomInt(*mut i32);
+struct SyncPointer(CustomInt);
+unsafe impl Sync for SyncPointer {}
+unsafe impl Send for CustomInt {}
+
+fn test_multi_traits_issues() {
+ let mut f1 = 10;
+ let f1 = CustomInt(&mut f1 as *mut i32);
+ let fptr1 = SyncPointer(f1);
+
+ let mut f2 = 10;
+ let fptr2 = SendPointer(&mut f2 as *mut i32);
+ thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe {
+ //~^ ERROR: changes to closure capture in Rust 2021
+ //~| NOTE: in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`
+ //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`
+ //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured
+ *fptr1.0.0 = 20;
+ //~^ NOTE: in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
+ *fptr2.0 = 20;
+ //~^ NOTE: in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0`
+ } });
+}
+
+fn main() {
+ test_multi_issues();
+ test_capturing_all_disjoint_fields_individually();
+ test_capturing_several_disjoint_fields_individually_1();
+ test_capturing_several_disjoint_fields_individually_2();
+ test_multi_traits_issues();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs
new file mode 100644
index 000000000..cfc4555ca
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs
@@ -0,0 +1,153 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+use std::thread;
+
+#[derive(Debug)]
+struct Foo(String);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+impl Foo {
+ fn from(s: &str) -> Self {
+ Self(String::from(s))
+ }
+}
+
+struct S(#[allow(unused_tuple_struct_fields)] Foo);
+
+#[derive(Clone)]
+struct T(#[allow(unused_tuple_struct_fields)] i32);
+
+struct U(S, T);
+
+impl Clone for U {
+ fn clone(&self) -> Self {
+ U(S(Foo::from("Hello World")), T(0))
+ }
+}
+
+fn test_multi_issues() {
+ let f1 = U(S(Foo::from("foo")), T(0));
+ let f2 = U(S(Foo::from("bar")), T(0));
+ let c = || {
+ //~^ ERROR: changes to closure capture in Rust 2021
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured
+ let _f_1 = f1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ let _f_2 = f2.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1`
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+//~^ NOTE: in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure
+
+fn test_capturing_all_disjoint_fields_individually() {
+ let f1 = U(S(Foo::from("foo")), T(0));
+ let c = || {
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f1` to be fully captured
+ let _f_1 = f1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ let _f_2 = f1.1;
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+
+struct U1(S, T, S);
+
+impl Clone for U1 {
+ fn clone(&self) -> Self {
+ U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")))
+ }
+}
+
+fn test_capturing_several_disjoint_fields_individually_1() {
+ let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
+ let c = || {
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f1` to be fully captured
+ let _f_0 = f1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ let _f_2 = f1.2;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2`
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+
+fn test_capturing_several_disjoint_fields_individually_2() {
+ let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
+ let c = || {
+ //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+ //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `f1` to be fully captured
+ let _f_0 = f1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ let _f_1 = f1.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1`
+ };
+
+ let c_clone = c.clone();
+
+ c_clone();
+}
+//~^ NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure
+
+struct SendPointer(*mut i32);
+unsafe impl Send for SendPointer {}
+
+struct CustomInt(*mut i32);
+struct SyncPointer(CustomInt);
+unsafe impl Sync for SyncPointer {}
+unsafe impl Send for CustomInt {}
+
+fn test_multi_traits_issues() {
+ let mut f1 = 10;
+ let f1 = CustomInt(&mut f1 as *mut i32);
+ let fptr1 = SyncPointer(f1);
+
+ let mut f2 = 10;
+ let fptr2 = SendPointer(&mut f2 as *mut i32);
+ thread::spawn(move || unsafe {
+ //~^ ERROR: changes to closure capture in Rust 2021
+ //~| NOTE: in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`
+ //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`
+ //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured
+ *fptr1.0.0 = 20;
+ //~^ NOTE: in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
+ *fptr2.0 = 20;
+ //~^ NOTE: in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0`
+ });
+}
+
+fn main() {
+ test_multi_issues();
+ test_capturing_all_disjoint_fields_individually();
+ test_capturing_several_disjoint_fields_individually_1();
+ test_capturing_several_disjoint_fields_individually_2();
+ test_multi_traits_issues();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr
new file mode 100644
index 000000000..96d5c936f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr
@@ -0,0 +1,118 @@
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+ --> $DIR/multi_diagnostics.rs:37:13
+ |
+LL | let c = || {
+ | ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+...
+LL | let _f_1 = f1.0;
+ | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+LL |
+LL | let _f_2 = f2.1;
+ | ---- in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1`
+...
+LL | }
+ | - in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure
+ |
+note: the lint level is defined here
+ --> $DIR/multi_diagnostics.rs:2:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f1`, `f2` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = (&f1, &f2);
+ |
+
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+ --> $DIR/multi_diagnostics.rs:56:13
+ |
+LL | let c = || {
+ | ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+...
+LL | let _f_1 = f1.0;
+ | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f1` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &f1;
+ |
+
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+ --> $DIR/multi_diagnostics.rs:81:13
+ |
+LL | let c = || {
+ | ^^
+ | |
+ | in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+ | in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.2` does not implement `Clone`
+...
+LL | let _f_0 = f1.0;
+ | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+LL |
+LL | let _f_2 = f1.2;
+ | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2`
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f1` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &f1;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+ --> $DIR/multi_diagnostics.rs:100:13
+ |
+LL | let c = || {
+ | ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+...
+LL | let _f_0 = f1.0;
+ | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+LL |
+LL | let _f_1 = f1.1;
+ | ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1`
+...
+LL | }
+ | -
+ | |
+ | in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure
+ | in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f1` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &f1;
+ |
+
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+ --> $DIR/multi_diagnostics.rs:133:19
+ |
+LL | thread::spawn(move || unsafe {
+ | ^^^^^^^
+ | |
+ | in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Send`
+ | in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Sync`
+ | in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr2` is not fully captured and `fptr2.0` does not implement `Send`
+...
+LL | *fptr1.0.0 = 20;
+ | ---------- in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
+LL |
+LL | *fptr2.0 = 20;
+ | -------- in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0`
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `fptr1`, `fptr2` to be fully captured
+ |
+LL ~ thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe {
+LL |
+ ...
+LL |
+LL ~ } });
+ |
+
+error: aborting due to 5 previous errors
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs
new file mode 100644
index 000000000..8b75e226a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs
@@ -0,0 +1,81 @@
+// run-pass
+
+// Set of test cases that don't need migrations
+
+#![deny(rust_2021_incompatible_closure_captures)]
+
+// Copy types as copied by the closure instead of being moved into the closure
+// Therefore their drop order isn't tied to the closure and won't be requiring any
+// migrations.
+fn test1_only_copy_types() {
+ let t = (0i32, 0i32);
+
+ let c = || {
+ let _t = t.0;
+ };
+
+ c();
+}
+
+// Same as test1 but using a move closure
+fn test2_only_copy_types_move_closure() {
+ let t = (0i32, 0i32);
+
+ let c = move || {
+ println!("{}", t.0);
+ };
+
+ c();
+}
+
+// Don't need to migrate if captured by ref
+fn test3_only_copy_types_move_closure() {
+ let t = (String::new(), String::new());
+
+ let c = || {
+ println!("{}", t.0);
+ };
+
+ c();
+}
+
+// Test migration analysis in case of Insignificant Drop + Non Drop aggregates.
+// Note in this test the closure captures a non Drop type and therefore the variable
+// is only captured by ref.
+fn test4_insignificant_drop_non_drop_aggregate() {
+ let t = (String::new(), 0i32);
+
+ let c = || {
+ let _t = t.1;
+ };
+
+ c();
+}
+
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+// Test migration analysis in case of Significant Drop + Non Drop aggregates.
+// Note in this test the closure captures a non Drop type and therefore the variable
+// is only captured by ref.
+fn test5_significant_drop_non_drop_aggregate() {
+ let t = (Foo(0), 0i32);
+
+ let c = || {
+ let _t = t.1;
+ };
+
+ c();
+}
+
+fn main() {
+ test1_only_copy_types();
+ test2_only_copy_types_move_closure();
+ test3_only_copy_types_move_closure();
+ test4_insignificant_drop_non_drop_aggregate();
+ test5_significant_drop_non_drop_aggregate();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/old_name.rs b/src/test/ui/closures/2229_closure_analysis/migrations/old_name.rs
new file mode 100644
index 000000000..16e3cca7b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/old_name.rs
@@ -0,0 +1,9 @@
+// check-pass
+
+// Ensure that the old name for `rust_2021_incompatible_closure_captures` is still
+// accepted by the compiler
+
+#![allow(disjoint_capture_migration)]
+//~^ WARN lint `disjoint_capture_migration` has been renamed
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/old_name.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/old_name.stderr
new file mode 100644
index 000000000..47cb689fa
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/old_name.stderr
@@ -0,0 +1,10 @@
+warning: lint `disjoint_capture_migration` has been renamed to `rust_2021_incompatible_closure_captures`
+ --> $DIR/old_name.rs:6:10
+ |
+LL | #![allow(disjoint_capture_migration)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rust_2021_incompatible_closure_captures`
+ |
+ = note: `#[warn(renamed_and_removed_lints)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed
new file mode 100644
index 000000000..7892a72c7
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed
@@ -0,0 +1,69 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+struct ConstainsDropField(Foo, Foo);
+
+// Test that lint is triggered if a path that implements Drop is not captured by move
+fn test_precise_analysis_drop_paths_not_captured_by_move() {
+ let t = ConstainsDropField(Foo(10), Foo(20));
+
+ let c = || {
+ let _ = &t;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ let _t = &t.1;
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+struct S;
+impl Drop for S {
+ fn drop(&mut self) {}
+}
+
+struct T(S, S);
+struct U(T, T);
+
+// Test precise analysis for the lint works with paths longer than one.
+fn test_precise_analysis_long_path_missing() {
+ let u = U(T(S, S), T(S, S));
+
+ let c = || {
+ let _ = &u;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `u` to be fully captured
+ let _x = u.0.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0`
+ let _x = u.0.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1`
+ let _x = u.1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure
+
+
+fn main() {
+ test_precise_analysis_drop_paths_not_captured_by_move();
+ test_precise_analysis_long_path_missing();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs
new file mode 100644
index 000000000..f5e99002b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs
@@ -0,0 +1,67 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+struct ConstainsDropField(Foo, Foo);
+
+// Test that lint is triggered if a path that implements Drop is not captured by move
+fn test_precise_analysis_drop_paths_not_captured_by_move() {
+ let t = ConstainsDropField(Foo(10), Foo(20));
+
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ let _t = &t.1;
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+struct S;
+impl Drop for S {
+ fn drop(&mut self) {}
+}
+
+struct T(S, S);
+struct U(T, T);
+
+// Test precise analysis for the lint works with paths longer than one.
+fn test_precise_analysis_long_path_missing() {
+ let u = U(T(S, S), T(S, S));
+
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `u` to be fully captured
+ let _x = u.0.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0`
+ let _x = u.0.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1`
+ let _x = u.1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure
+
+
+fn main() {
+ test_precise_analysis_drop_paths_not_captured_by_move();
+ test_precise_analysis_long_path_missing();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr
new file mode 100644
index 000000000..aa9b8672a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr
@@ -0,0 +1,55 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/precise.rs:20:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.0;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ |
+note: the lint level is defined here
+ --> $DIR/precise.rs:3:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &t;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/precise.rs:45:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _x = u.0.0;
+ | ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0`
+LL |
+LL | let _x = u.0.1;
+ | ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1`
+LL |
+LL | let _x = u.1.0;
+ | ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0`
+...
+LL | }
+ | -
+ | |
+ | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure
+ | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure
+ | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `u` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &u;
+ |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs
new file mode 100644
index 000000000..587d71c40
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs
@@ -0,0 +1,104 @@
+// run-pass
+
+#![deny(rust_2021_incompatible_closure_captures)]
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+struct ConstainsDropField(Foo, Foo);
+
+// Test that if all paths starting at root variable that implement Drop are captured
+// then it doesn't trigger the lint.
+fn test_precise_analysis_simple_1() {
+ let t = (Foo(10), Foo(20), Foo(30));
+
+ let c = || {
+ let _t = t.0;
+ let _t = t.1;
+ let _t = t.2;
+ };
+
+ c();
+}
+
+// Test that if all paths starting at root variable that implement Drop are captured
+// then it doesn't trigger the lint.
+fn test_precise_analysis_simple_2() {
+ let t = ConstainsDropField(Foo(10), Foo(20));
+
+ let c = || {
+ let _t = t.0;
+ let _t = t.1;
+ };
+
+ c();
+}
+
+#[derive(Debug)]
+struct ContainsAndImplsDrop(Foo);
+impl Drop for ContainsAndImplsDrop {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+// If a path isn't directly captured but requires Drop, then this tests that migrations aren't
+// needed if the a parent to that path is captured.
+fn test_precise_analysis_parent_captured_1() {
+ let t = ConstainsDropField(Foo(10), Foo(20));
+
+ let c = || {
+ let _t = t;
+ };
+
+ c();
+}
+
+// If a path isn't directly captured but requires Drop, then this tests that migrations aren't
+// needed if the a parent to that path is captured.
+fn test_precise_analysis_parent_captured_2() {
+ let t = ContainsAndImplsDrop(Foo(10));
+
+ let c = || {
+ let _t = t;
+ };
+
+ c();
+}
+
+struct S;
+impl Drop for S {
+ fn drop(&mut self) {}
+}
+
+struct T(S, S);
+struct U(T, T);
+
+// Test that if the path is longer than just one element, precise analysis works correctly.
+fn test_precise_analysis_long_path() {
+ let u = U(T(S, S), T(S, S));
+
+ let c = || {
+ let _x = u.0.0;
+ let _x = u.0.1;
+ let _x = u.1.0;
+ let _x = u.1.1;
+ };
+
+ c();
+}
+
+fn main() {
+ test_precise_analysis_simple_1();
+ test_precise_analysis_simple_2();
+
+ test_precise_analysis_parent_captured_1();
+ test_precise_analysis_parent_captured_2();
+
+ test_precise_analysis_long_path();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed
new file mode 100644
index 000000000..e99dbb5ab
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed
@@ -0,0 +1,229 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+// Test cases for types that implement a significant drop (user defined)
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+#[derive(Debug)]
+struct ConstainsDropField(Foo, #[allow(unused_tuple_struct_fields)] Foo);
+
+// `t` needs Drop because one of its elements needs drop,
+// therefore precise capture might affect drop ordering
+fn test1_all_need_migration() {
+ let t = (Foo(0), Foo(0));
+ let t1 = (Foo(0), Foo(0));
+ let t2 = (Foo(0), Foo(0));
+
+ let c = || {
+ let _ = (&t, &t1, &t2);
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ let _t1 = t1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+ let _t2 = t2.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure
+
+// String implements drop and therefore should be migrated.
+// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
+fn test2_only_precise_paths_need_migration() {
+ let t = (Foo(0), Foo(0));
+ let t1 = (Foo(0), Foo(0));
+ let t2 = (Foo(0), Foo(0));
+
+ let c = || {
+ let _ = (&t, &t1);
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ let _t1 = t1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+ let _t2 = t2;
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+
+// If a variable would've not been captured by value then it would've not been
+// dropped with the closure and therefore doesn't need migration.
+fn test3_only_by_value_need_migration() {
+ let t = (Foo(0), Foo(0));
+ let t1 = (Foo(0), Foo(0));
+ let c = || {
+ let _ = &t;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ println!("{:?}", t1.1);
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// The root variable might not implement drop themselves but some path starting
+// at the root variable might implement Drop.
+//
+// If this path isn't captured we need to migrate for the root variable.
+fn test4_type_contains_drop_need_migration() {
+ let t = ConstainsDropField(Foo(0), Foo(0));
+
+ let c = || {
+ let _ = &t;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Test migration analysis in case of Drop + Non Drop aggregates.
+// Note we need migration here only because the non-copy (because Drop type) is captured,
+// otherwise we won't need to, since we can get away with just by ref capture in that case.
+fn test5_drop_non_drop_aggregate_need_migration() {
+ let t = (Foo(0), Foo(0), 0i32);
+
+ let c = || {
+ let _ = &t;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Test migration analysis in case of Significant and Insignificant Drop aggregates.
+fn test6_significant_insignificant_drop_aggregate_need_migration() {
+ let t = (Foo(0), String::new());
+
+ let c = || {
+ let _ = &t;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+
+// Since we are using a move closure here, both `t` and `t1` get moved
+// even though they are being used by ref inside the closure.
+fn test7_move_closures_non_copy_types_might_need_migration() {
+ let t = (Foo(0), Foo(0));
+ let t1 = (Foo(0), Foo(0), Foo(0));
+
+ let c = move || {
+ let _ = (&t1, &t);
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured
+ println!("{:?} {:?}", t1.1, t.1);
+ //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1`
+ //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure
+
+
+fn test8_drop_order_and_blocks() {
+ {
+ let tuple =
+ (Foo(0), Foo(1));
+ {
+ let c = || {
+ let _ = &tuple;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `tuple` to be fully captured
+ tuple.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+ };
+
+ c();
+ }
+ //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+ }
+}
+
+fn test9_drop_order_and_nested_closures() {
+ let tuple =
+ (Foo(0), Foo(1));
+ let b = || {
+ let c = || {
+ let _ = &tuple;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `tuple` to be fully captured
+ tuple.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+ };
+
+ c();
+ };
+ //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+
+ b();
+}
+
+// Test that we migrate if drop order of Vec<T> would be affected if T is a significant drop type
+fn test10_vec_of_significant_drop_type() {
+
+ let tup = (Foo(0), vec![Foo(3)]);
+
+ let _c = || { let _ = &tup; tup.0 };
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `tup` to be fully captured
+ //~| NOTE: in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0`
+}
+//~^ NOTE: in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure
+
+fn main() {
+ test1_all_need_migration();
+ test2_only_precise_paths_need_migration();
+ test3_only_by_value_need_migration();
+ test4_type_contains_drop_need_migration();
+ test5_drop_non_drop_aggregate_need_migration();
+ test6_significant_insignificant_drop_aggregate_need_migration();
+ test7_move_closures_non_copy_types_might_need_migration();
+ test8_drop_order_and_blocks();
+ test9_drop_order_and_nested_closures();
+ test10_vec_of_significant_drop_type();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
new file mode 100644
index 000000000..62a984c9e
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
@@ -0,0 +1,220 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+// Test cases for types that implement a significant drop (user defined)
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("{:?} dropped", self.0);
+ }
+}
+
+#[derive(Debug)]
+struct ConstainsDropField(Foo, #[allow(unused_tuple_struct_fields)] Foo);
+
+// `t` needs Drop because one of its elements needs drop,
+// therefore precise capture might affect drop ordering
+fn test1_all_need_migration() {
+ let t = (Foo(0), Foo(0));
+ let t1 = (Foo(0), Foo(0));
+ let t2 = (Foo(0), Foo(0));
+
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ let _t1 = t1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+ let _t2 = t2.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure
+
+// String implements drop and therefore should be migrated.
+// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
+fn test2_only_precise_paths_need_migration() {
+ let t = (Foo(0), Foo(0));
+ let t1 = (Foo(0), Foo(0));
+ let t2 = (Foo(0), Foo(0));
+
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ let _t1 = t1.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+ let _t2 = t2;
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+
+// If a variable would've not been captured by value then it would've not been
+// dropped with the closure and therefore doesn't need migration.
+fn test3_only_by_value_need_migration() {
+ let t = (Foo(0), Foo(0));
+ let t1 = (Foo(0), Foo(0));
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ println!("{:?}", t1.1);
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// The root variable might not implement drop themselves but some path starting
+// at the root variable might implement Drop.
+//
+// If this path isn't captured we need to migrate for the root variable.
+fn test4_type_contains_drop_need_migration() {
+ let t = ConstainsDropField(Foo(0), Foo(0));
+
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Test migration analysis in case of Drop + Non Drop aggregates.
+// Note we need migration here only because the non-copy (because Drop type) is captured,
+// otherwise we won't need to, since we can get away with just by ref capture in that case.
+fn test5_drop_non_drop_aggregate_need_migration() {
+ let t = (Foo(0), Foo(0), 0i32);
+
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Test migration analysis in case of Significant and Insignificant Drop aggregates.
+fn test6_significant_insignificant_drop_aggregate_need_migration() {
+ let t = (Foo(0), String::new());
+
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t` to be fully captured
+ let _t = t.1;
+ //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+
+// Since we are using a move closure here, both `t` and `t1` get moved
+// even though they are being used by ref inside the closure.
+fn test7_move_closures_non_copy_types_might_need_migration() {
+ let t = (Foo(0), Foo(0));
+ let t1 = (Foo(0), Foo(0), Foo(0));
+
+ let c = move || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured
+ println!("{:?} {:?}", t1.1, t.1);
+ //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1`
+ //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+ };
+
+ c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure
+
+
+fn test8_drop_order_and_blocks() {
+ {
+ let tuple =
+ (Foo(0), Foo(1));
+ {
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `tuple` to be fully captured
+ tuple.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+ };
+
+ c();
+ }
+ //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+ }
+}
+
+fn test9_drop_order_and_nested_closures() {
+ let tuple =
+ (Foo(0), Foo(1));
+ let b = || {
+ let c = || {
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `tuple` to be fully captured
+ tuple.0;
+ //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+ };
+
+ c();
+ };
+ //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+
+ b();
+}
+
+// Test that we migrate if drop order of Vec<T> would be affected if T is a significant drop type
+fn test10_vec_of_significant_drop_type() {
+
+ let tup = (Foo(0), vec![Foo(3)]);
+
+ let _c = || tup.0;
+ //~^ ERROR: drop order
+ //~| NOTE: for more information, see
+ //~| HELP: add a dummy let to cause `tup` to be fully captured
+ //~| NOTE: in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0`
+}
+//~^ NOTE: in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure
+
+fn main() {
+ test1_all_need_migration();
+ test2_only_precise_paths_need_migration();
+ test3_only_by_value_need_migration();
+ test4_type_contains_drop_need_migration();
+ test5_drop_non_drop_aggregate_need_migration();
+ test6_significant_insignificant_drop_aggregate_need_migration();
+ test7_move_closures_non_copy_types_might_need_migration();
+ test8_drop_order_and_blocks();
+ test9_drop_order_and_nested_closures();
+ test10_vec_of_significant_drop_type();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
new file mode 100644
index 000000000..0d9f09ee3
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
@@ -0,0 +1,214 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:25:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.0;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+LL |
+LL | let _t1 = t1.0;
+ | ---- in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+LL |
+LL | let _t2 = t2.0;
+ | ---- in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0`
+...
+LL | }
+ | -
+ | |
+ | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+ | in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure
+ |
+note: the lint level is defined here
+ --> $DIR/significant_drop.rs:2:9
+ |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t`, `t1`, `t2` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = (&t, &t1, &t2);
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:50:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.0;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+LL |
+LL | let _t1 = t1.0;
+ | ---- in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+...
+LL | }
+ | -
+ | |
+ | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t`, `t1` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = (&t, &t1);
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:71:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.0;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &t;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:91:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.0;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &t;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:109:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.0;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &t;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:125:13
+ |
+LL | let c = || {
+ | ^^
+...
+LL | let _t = t.1;
+ | --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+...
+LL | }
+ | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &t;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:143:13
+ |
+LL | let c = move || {
+ | ^^^^^^^
+...
+LL | println!("{:?} {:?}", t1.1, t.1);
+ | ---- --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+ | |
+ | in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1`
+...
+LL | }
+ | -
+ | |
+ | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure
+ | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t1`, `t` to be fully captured
+ |
+LL ~ let c = move || {
+LL + let _ = (&t1, &t);
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:163:21
+ |
+LL | let c = || {
+ | ^^
+...
+LL | tuple.0;
+ | ------- in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+...
+LL | }
+ | - in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `tuple` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &tuple;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:181:17
+ |
+LL | let c = || {
+ | ^^
+...
+LL | tuple.0;
+ | ------- in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+...
+LL | };
+ | - in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `tuple` to be fully captured
+ |
+LL ~ let c = || {
+LL + let _ = &tuple;
+ |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+ --> $DIR/significant_drop.rs:201:18
+ |
+LL | let _c = || tup.0;
+ | ^^ ----- in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0`
+...
+LL | }
+ | - in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure
+ |
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `tup` to be fully captured
+ |
+LL | let _c = || { let _ = &tup; tup.0 };
+ | +++++++++++++++ +
+
+error: aborting due to 10 previous errors
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs b/src/test/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs
new file mode 100644
index 000000000..39cf82053
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs
@@ -0,0 +1,13 @@
+//run-pass
+#![deny(rust_2021_incompatible_closure_captures)]
+#![allow(unused_must_use)]
+
+fn filter_try_fold(
+ predicate: &mut impl FnMut() -> bool,
+) -> impl FnMut() -> bool + '_ {
+ move || predicate()
+}
+
+fn main() {
+ filter_try_fold(&mut || true);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/move_closure.rs
new file mode 100644
index 000000000..b542fa243
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/move_closure.rs
@@ -0,0 +1,222 @@
+// edition:2021
+
+// Test that move closures drop derefs with `capture_disjoint_fields` enabled.
+
+#![feature(rustc_attrs)]
+
+fn simple_move_closure() {
+ struct S(String);
+ struct T(S);
+
+ let t = T(S("s".into()));
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ move || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ t.0.0 = "new S".into();
+ //~^ NOTE: Capturing t[(0, 0),(0, 0)] -> MutBorrow
+ //~| NOTE: Min Capture t[(0, 0),(0, 0)] -> ByValue
+ };
+ c();
+}
+
+// Test move closure use reborrows when using references
+fn simple_ref() {
+ let mut s = 10;
+ let ref_s = &mut s;
+
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ move || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ *ref_s += 10;
+ //~^ NOTE: Capturing ref_s[Deref] -> MutBorrow
+ //~| NOTE: Min Capture ref_s[] -> ByValue
+ };
+ c();
+}
+
+// Test move closure use reborrows when using references
+fn struct_contains_ref_to_another_struct_1() {
+ struct S(String);
+ struct T<'a>(&'a mut S);
+
+ let mut s = S("s".into());
+ let t = T(&mut s);
+
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ move || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ t.0.0 = "new s".into();
+ //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> MutBorrow
+ //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+ };
+
+ c();
+}
+
+// Test that we can use reborrows to read data of Copy types
+// i.e. without truncating derefs
+fn struct_contains_ref_to_another_struct_2() {
+ struct S(i32);
+ struct T<'a>(&'a S);
+
+ let s = S(0);
+ let t = T(&s);
+
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ move || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let _t = t.0.0;
+ //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+ };
+
+ c();
+}
+
+// Test that we can use truncate to move out of !Copy types
+fn struct_contains_ref_to_another_struct_3() {
+ struct S(String);
+ struct T<'a>(&'a S);
+
+ let s = S("s".into());
+ let t = T(&s);
+
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ move || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let _t = t.0.0;
+ //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+ //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+ };
+
+ c();
+}
+
+// Test that derefs of box are truncated in move closures
+fn truncate_box_derefs() {
+ struct S(i32);
+
+
+ // Content within the box is moved within the closure
+ let b = Box::new(S(10));
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ move || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let _t = b.0;
+ //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture b[] -> ByValue
+ };
+
+ c();
+
+ // Content within the box is used by a shared ref and the box is the root variable
+ let b = Box::new(S(10));
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ move || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{}", b.0);
+ //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture b[] -> ByValue
+ };
+
+ c();
+
+ // Content within the box is used by a shared ref and the box is not the root variable
+ let b = Box::new(S(10));
+ let t = (0, b);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ move || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{}", t.1.0);
+ //~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture t[(1, 0)] -> ByValue
+ };
+}
+
+struct Foo { x: i32 }
+
+// Ensure that even in move closures, if the data is not owned by the root variable
+// then we don't truncate the derefs or a ByValue capture, rather do a reborrow
+fn box_mut_1() {
+ let mut foo = Foo { x: 0 } ;
+
+ let p_foo = &mut foo;
+ let box_p_foo = Box::new(p_foo);
+
+ let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ //~| First Pass analysis includes:
+ //~| NOTE: Capturing box_p_foo[Deref,Deref,(0, 0)] -> MutBorrow
+ //~| Min Capture analysis includes:
+ //~| NOTE: Min Capture box_p_foo[] -> ByValue
+}
+
+// Ensure that even in move closures, if the data is not owned by the root variable
+// then we don't truncate the derefs or a ByValue capture, rather do a reborrow
+fn box_mut_2() {
+ let foo = Foo { x: 0 } ;
+
+ let mut box_foo = Box::new(foo);
+ let p_foo = &mut box_foo;
+
+ let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ //~| First Pass analysis includes:
+ //~| NOTE: Capturing p_foo[Deref,Deref,(0, 0)] -> MutBorrow
+ //~| Min Capture analysis includes:
+ //~| NOTE: Min Capture p_foo[] -> ByValue
+}
+
+// Test that move closures can take ownership of Copy type
+fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
+ let x = 10;
+
+ let c = #[rustc_capture_analysis] move || x;
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ //~| First Pass analysis includes:
+ //~| NOTE: Capturing x[] -> ImmBorrow
+ //~| Min Capture analysis includes:
+ //~| NOTE: Min Capture x[] -> ByValue
+
+ c
+}
+
+fn main() {
+ simple_move_closure();
+ simple_ref();
+ struct_contains_ref_to_another_struct_1();
+ struct_contains_ref_to_another_struct_2();
+ struct_contains_ref_to_another_struct_3();
+ truncate_box_derefs();
+ box_mut_2();
+ box_mut_1();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr
new file mode 100644
index 000000000..fd80e05c6
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr
@@ -0,0 +1,462 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:12:17
+ |
+LL | let mut 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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:30:17
+ |
+LL | let mut 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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:51:17
+ |
+LL | let mut 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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:74:17
+ |
+LL | let mut 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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:96:17
+ |
+LL | let mut 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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:117: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:133: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:150: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:172:13
+ |
+LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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/move_closure.rs:189:13
+ |
+LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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/move_closure.rs:202:13
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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: First Pass analysis includes:
+ --> $DIR/move_closure.rs:202:39
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^^^^^^^^^
+ |
+note: Capturing x[] -> ImmBorrow
+ --> $DIR/move_closure.rs:202:47
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:202:39
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^^^^^^^^^
+ |
+note: Min Capture x[] -> ByValue
+ --> $DIR/move_closure.rs:202:47
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:15:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | t.0.0 = "new S".into();
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0),(0, 0)] -> MutBorrow
+ --> $DIR/move_closure.rs:18:9
+ |
+LL | t.0.0 = "new S".into();
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:15:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | t.0.0 = "new S".into();
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0),(0, 0)] -> ByValue
+ --> $DIR/move_closure.rs:18:9
+ |
+LL | t.0.0 = "new S".into();
+ | ^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:33:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | *ref_s += 10;
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing ref_s[Deref] -> MutBorrow
+ --> $DIR/move_closure.rs:36:9
+ |
+LL | *ref_s += 10;
+ | ^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:33:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | *ref_s += 10;
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture ref_s[] -> ByValue
+ --> $DIR/move_closure.rs:36:9
+ |
+LL | *ref_s += 10;
+ | ^^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:54:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | t.0.0 = "new s".into();
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> MutBorrow
+ --> $DIR/move_closure.rs:57:9
+ |
+LL | t.0.0 = "new s".into();
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:54:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | t.0.0 = "new s".into();
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0)] -> ByValue
+ --> $DIR/move_closure.rs:57:9
+ |
+LL | t.0.0 = "new s".into();
+ | ^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:77:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | let _t = t.0.0;
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/move_closure.rs:80:18
+ |
+LL | let _t = t.0.0;
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:77:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | let _t = t.0.0;
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0)] -> ByValue
+ --> $DIR/move_closure.rs:80:18
+ |
+LL | let _t = t.0.0;
+ | ^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:99:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | let _t = t.0.0;
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+ --> $DIR/move_closure.rs:102:18
+ |
+LL | let _t = t.0.0;
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:99:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | let _t = t.0.0;
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0)] -> ByValue
+ --> $DIR/move_closure.rs:102:18
+ |
+LL | let _t = t.0.0;
+ | ^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:120:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | let _t = b.0;
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing b[Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/move_closure.rs:123:18
+ |
+LL | let _t = b.0;
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:120:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | let _t = b.0;
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture b[] -> ByValue
+ --> $DIR/move_closure.rs:123:18
+ |
+LL | let _t = b.0;
+ | ^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:136:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | println!("{}", b.0);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing b[Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/move_closure.rs:139:24
+ |
+LL | println!("{}", b.0);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:136:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | println!("{}", b.0);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture b[] -> ByValue
+ --> $DIR/move_closure.rs:139:24
+ |
+LL | println!("{}", b.0);
+ | ^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:153:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | println!("{}", t.1.0);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/move_closure.rs:156:24
+ |
+LL | println!("{}", t.1.0);
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:153:5
+ |
+LL | / move || {
+LL | |
+LL | |
+LL | | println!("{}", t.1.0);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(1, 0)] -> ByValue
+ --> $DIR/move_closure.rs:156:24
+ |
+LL | println!("{}", t.1.0);
+ | ^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:172:39
+ |
+LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: Capturing box_p_foo[Deref,Deref,(0, 0)] -> MutBorrow
+ --> $DIR/move_closure.rs:172:47
+ |
+LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+ | ^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:172:39
+ |
+LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: Min Capture box_p_foo[] -> ByValue
+ --> $DIR/move_closure.rs:172:47
+ |
+LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+ | ^^^^^^^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:189:39
+ |
+LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+note: Capturing p_foo[Deref,Deref,(0, 0)] -> MutBorrow
+ --> $DIR/move_closure.rs:189:47
+ |
+LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+ | ^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:189:39
+ |
+LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+note: Min Capture p_foo[] -> ByValue
+ --> $DIR/move_closure.rs:189:47
+ |
+LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+ | ^^^^^^^
+
+error: aborting due to 33 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs
new file mode 100644
index 000000000..a8a2acfa7
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs
@@ -0,0 +1,39 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+struct Wrapper {
+ p: Point,
+}
+
+fn main() {
+ let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+ // Only paths that appears within the closure that directly start off
+ // a variable defined outside the closure are captured.
+ //
+ // Therefore `w.p` is captured
+ // Note that `wp.x` doesn't start off a variable defined outside the closure.
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let wp = &w.p;
+ //~^ NOTE: Capturing w[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture w[(0, 0)] -> ImmBorrow
+ println!("{}", wp.x);
+ };
+
+ // Since `c` captures `w.p` by an ImmBorrow, `w.p.y` can't be mutated.
+ let py = &mut w.p.y;
+ c();
+
+ *py = 20
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr
new file mode 100644
index 000000000..29ad1c591
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/multilevel-path-1.rs:22: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
+
+error: First Pass analysis includes:
+ --> $DIR/multilevel-path-1.rs:25:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let wp = &w.p;
+... |
+LL | | println!("{}", wp.x);
+LL | | };
+ | |_____^
+ |
+note: Capturing w[(0, 0)] -> ImmBorrow
+ --> $DIR/multilevel-path-1.rs:28:19
+ |
+LL | let wp = &w.p;
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/multilevel-path-1.rs:25:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let wp = &w.p;
+... |
+LL | | println!("{}", wp.x);
+LL | | };
+ | |_____^
+ |
+note: Min Capture w[(0, 0)] -> ImmBorrow
+ --> $DIR/multilevel-path-1.rs:28:19
+ |
+LL | let wp = &w.p;
+ | ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs
new file mode 100644
index 000000000..e21fe318c
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+struct Wrapper {
+ p: Point,
+}
+
+fn main() {
+ let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{}", w.p.x);
+ //~^ NOTE: Capturing w[(0, 0),(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow
+ };
+
+ // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`.
+ let py = &mut w.p.y;
+ c();
+
+ *py = 20
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr
new file mode 100644
index 000000000..929cba113
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/multilevel-path-2.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
+
+error: First Pass analysis includes:
+ --> $DIR/multilevel-path-2.rs:20:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", w.p.x);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing w[(0, 0),(0, 0)] -> ImmBorrow
+ --> $DIR/multilevel-path-2.rs:23:24
+ |
+LL | println!("{}", w.p.x);
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/multilevel-path-2.rs:20:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", w.p.x);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow
+ --> $DIR/multilevel-path-2.rs:23:24
+ |
+LL | println!("{}", w.p.x);
+ | ^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs
new file mode 100644
index 000000000..22eae744b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs
@@ -0,0 +1,53 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+// This testcase ensures that nested closures are handles properly
+// - The nested closure is analyzed first.
+// - The capture kind of the nested closure is accounted for by the enclosing closure
+// - Any captured path by the nested closure that starts off a local variable in the enclosing
+// closure is not listed as a capture of the enclosing closure.
+
+fn main() {
+ let mut p = Point { x: 5, y: 20 };
+
+ let mut c1 = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{}", p.x);
+ //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
+ let incr = 10;
+ let mut c2 = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || p.y += incr;
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ //~| NOTE: Capturing p[(1, 0)] -> MutBorrow
+ //~| NOTE: Capturing incr[] -> ImmBorrow
+ //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow
+ //~| NOTE: Min Capture incr[] -> ImmBorrow
+ //~| NOTE: Capturing p[(1, 0)] -> MutBorrow
+ //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow
+ c2();
+ println!("{}", p.y);
+ //~^ NOTE: Capturing p[(1, 0)] -> ImmBorrow
+ };
+
+ c1();
+
+ let px = &p.x;
+
+ println!("{}", px);
+
+ c1();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr
new file mode 100644
index 000000000..a50d0c6a1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr
@@ -0,0 +1,106 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/nested-closure.rs:19:18
+ |
+LL | let mut c1 = #[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/nested-closure.rs:29:22
+ |
+LL | let mut c2 = #[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: First Pass analysis includes:
+ --> $DIR/nested-closure.rs:32:9
+ |
+LL | || p.y += incr;
+ | ^^^^^^^^^^^^^^
+ |
+note: Capturing p[(1, 0)] -> MutBorrow
+ --> $DIR/nested-closure.rs:32:12
+ |
+LL | || p.y += incr;
+ | ^^^
+note: Capturing incr[] -> ImmBorrow
+ --> $DIR/nested-closure.rs:32:19
+ |
+LL | || p.y += incr;
+ | ^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/nested-closure.rs:32:9
+ |
+LL | || p.y += incr;
+ | ^^^^^^^^^^^^^^
+ |
+note: Min Capture p[(1, 0)] -> MutBorrow
+ --> $DIR/nested-closure.rs:32:12
+ |
+LL | || p.y += incr;
+ | ^^^
+note: Min Capture incr[] -> ImmBorrow
+ --> $DIR/nested-closure.rs:32:19
+ |
+LL | || p.y += incr;
+ | ^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/nested-closure.rs:22:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", p.x);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing p[(0, 0)] -> ImmBorrow
+ --> $DIR/nested-closure.rs:25:24
+ |
+LL | println!("{}", p.x);
+ | ^^^
+note: Capturing p[(1, 0)] -> MutBorrow
+ --> $DIR/nested-closure.rs:32:12
+ |
+LL | || p.y += incr;
+ | ^^^
+note: Capturing p[(1, 0)] -> ImmBorrow
+ --> $DIR/nested-closure.rs:42:24
+ |
+LL | println!("{}", p.y);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/nested-closure.rs:22:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", p.x);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[(0, 0)] -> ImmBorrow
+ --> $DIR/nested-closure.rs:25:24
+ |
+LL | println!("{}", p.x);
+ | ^^^
+note: Min Capture p[(1, 0)] -> MutBorrow
+ --> $DIR/nested-closure.rs:32:12
+ |
+LL | || p.y += incr;
+ | ^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs
new file mode 100644
index 000000000..e7edc0bbc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs
@@ -0,0 +1,34 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+#![allow(dead_code)]
+
+struct Int(i32);
+struct B<'a>(&'a i32);
+
+const I : Int = Int(0);
+const REF_I : &'static Int = &I;
+
+
+struct MyStruct<'a> {
+ a: &'static Int,
+ b: B<'a>,
+}
+
+fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
+ let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ //~| ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ //~| NOTE: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow
+ c
+}
+
+fn main() {
+ let t = 0;
+ let s = MyStruct { a: REF_I, b: B(&t) };
+ let _ = foo(&s);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr
new file mode 100644
index 000000000..87d5d5bee
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr
@@ -0,0 +1,36 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/edge_case.rs:20:13
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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: First Pass analysis includes:
+ --> $DIR/edge_case.rs:20:39
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^^^^^^^^^^^
+ |
+note: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/edge_case.rs:20:48
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/edge_case.rs:20:39
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^^^^^^^^^^^
+ |
+note: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow
+ --> $DIR/edge_case.rs:20:48
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs
new file mode 100644
index 000000000..033fd6f17
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs
@@ -0,0 +1,27 @@
+// edition:2021
+// run-pass
+
+#![allow(unused)]
+#![allow(dead_code)]
+
+struct Int(i32);
+struct B<'a>(&'a i32);
+
+const I : Int = Int(0);
+const REF_I : &'static Int = &I;
+
+struct MyStruct<'a> {
+ a: &'static Int,
+ b: B<'a>,
+}
+
+fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
+ let c = || drop(&m.a.0);
+ c
+}
+
+fn main() {
+ let t = 0;
+ let s = MyStruct { a: REF_I, b: B(&t) };
+ let _ = foo(&s);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs
new file mode 100644
index 000000000..0c1031931
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+struct Point {
+ x: f32,
+ y: f32,
+}
+
+struct Pentagon {
+ points: [Point; 5],
+}
+
+fn main() {
+ let p1 = Point { x: 10.0, y: 10.0 };
+ let p2 = Point { x: 7.5, y: 12.5 };
+ let p3 = Point { x: 15.0, y: 15.0 };
+ let p4 = Point { x: 12.5, y: 12.5 };
+ let p5 = Point { x: 20.0, y: 10.0 };
+
+ let pent = Pentagon { points: [p1, p2, p3, p4, p5] };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{}", pent.points[5].x);
+ //~^ NOTE: Capturing pent[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture pent[(0, 0)] -> ImmBorrow
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr
new file mode 100644
index 000000000..124b7bf6f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/path-with-array-access.rs:23: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
+
+error: First Pass analysis includes:
+ --> $DIR/path-with-array-access.rs:26:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", pent.points[5].x);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing pent[(0, 0)] -> ImmBorrow
+ --> $DIR/path-with-array-access.rs:29:24
+ |
+LL | println!("{}", pent.points[5].x);
+ | ^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/path-with-array-access.rs:26:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", pent.points[5].x);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture pent[(0, 0)] -> ImmBorrow
+ --> $DIR/path-with-array-access.rs:29:24
+ |
+LL | println!("{}", pent.points[5].x);
+ | ^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs
new file mode 100644
index 000000000..2f8cddc06
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs
@@ -0,0 +1,101 @@
+// edition:2021
+
+// Tests that in cases where we individually capture all the fields of a type,
+// we still drop them in the order they would have been dropped in the 2018 edition.
+
+// NOTE: It is *critical* that the order of the min capture NOTES in the stderr output
+// does *not* change!
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct HasDrop;
+impl Drop for HasDrop {
+ fn drop(&mut self) {
+ println!("dropped");
+ }
+}
+
+fn test_one() {
+ let a = (HasDrop, HasDrop);
+ let b = (HasDrop, HasDrop);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: Min Capture analysis includes:
+ //~| ERROR
+ println!("{:?}", a.0);
+ //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow
+ //~| NOTE
+ println!("{:?}", a.1);
+ //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow
+ //~| NOTE
+
+ println!("{:?}", b.0);
+ //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow
+ //~| NOTE
+ println!("{:?}", b.1);
+ //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow
+ //~| NOTE
+ };
+}
+
+fn test_two() {
+ let a = (HasDrop, HasDrop);
+ let b = (HasDrop, HasDrop);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: Min Capture analysis includes:
+ //~| ERROR
+ println!("{:?}", a.1);
+ //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow
+ //~| NOTE
+ println!("{:?}", a.0);
+ //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow
+ //~| NOTE
+
+ println!("{:?}", b.1);
+ //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow
+ //~| NOTE
+ println!("{:?}", b.0);
+ //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow
+ //~| NOTE
+ };
+}
+
+fn test_three() {
+ let a = (HasDrop, HasDrop);
+ let b = (HasDrop, HasDrop);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: Min Capture analysis includes:
+ //~| ERROR
+ println!("{:?}", b.1);
+ //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow
+ //~| NOTE
+ println!("{:?}", a.1);
+ //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow
+ //~| NOTE
+ println!("{:?}", a.0);
+ //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow
+ //~| NOTE
+
+ println!("{:?}", b.0);
+ //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow
+ //~| NOTE
+ };
+}
+
+fn main() {
+ test_one();
+ test_two();
+ test_three();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr
new file mode 100644
index 000000000..2d1dc8727
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr
@@ -0,0 +1,228 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/preserve_field_drop_order.rs:23: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/preserve_field_drop_order.rs:49: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/preserve_field_drop_order.rs:75: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
+
+error: First Pass analysis includes:
+ --> $DIR/preserve_field_drop_order.rs:26:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{:?}", a.0);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing a[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:29:26
+ |
+LL | println!("{:?}", a.0);
+ | ^^^
+note: Capturing a[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:32:26
+ |
+LL | println!("{:?}", a.1);
+ | ^^^
+note: Capturing b[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:36:26
+ |
+LL | println!("{:?}", b.0);
+ | ^^^
+note: Capturing b[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:39:26
+ |
+LL | println!("{:?}", b.1);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/preserve_field_drop_order.rs:26:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{:?}", a.0);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture a[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:29:26
+ |
+LL | println!("{:?}", a.0);
+ | ^^^
+note: Min Capture a[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:32:26
+ |
+LL | println!("{:?}", a.1);
+ | ^^^
+note: Min Capture b[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:36:26
+ |
+LL | println!("{:?}", b.0);
+ | ^^^
+note: Min Capture b[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:39:26
+ |
+LL | println!("{:?}", b.1);
+ | ^^^
+
+error: First Pass analysis includes:
+ --> $DIR/preserve_field_drop_order.rs:52:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{:?}", a.1);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing a[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:55:26
+ |
+LL | println!("{:?}", a.1);
+ | ^^^
+note: Capturing a[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:58:26
+ |
+LL | println!("{:?}", a.0);
+ | ^^^
+note: Capturing b[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:62:26
+ |
+LL | println!("{:?}", b.1);
+ | ^^^
+note: Capturing b[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:65:26
+ |
+LL | println!("{:?}", b.0);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/preserve_field_drop_order.rs:52:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{:?}", a.1);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture a[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:58:26
+ |
+LL | println!("{:?}", a.0);
+ | ^^^
+note: Min Capture a[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:55:26
+ |
+LL | println!("{:?}", a.1);
+ | ^^^
+note: Min Capture b[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:65:26
+ |
+LL | println!("{:?}", b.0);
+ | ^^^
+note: Min Capture b[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:62:26
+ |
+LL | println!("{:?}", b.1);
+ | ^^^
+
+error: First Pass analysis includes:
+ --> $DIR/preserve_field_drop_order.rs:78:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{:?}", b.1);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing b[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:81:26
+ |
+LL | println!("{:?}", b.1);
+ | ^^^
+note: Capturing a[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:84:26
+ |
+LL | println!("{:?}", a.1);
+ | ^^^
+note: Capturing a[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:87:26
+ |
+LL | println!("{:?}", a.0);
+ | ^^^
+note: Capturing b[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:91:26
+ |
+LL | println!("{:?}", b.0);
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/preserve_field_drop_order.rs:78:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{:?}", b.1);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture b[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:91:26
+ |
+LL | println!("{:?}", b.0);
+ | ^^^
+note: Min Capture b[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:81:26
+ |
+LL | println!("{:?}", b.1);
+ | ^^^
+note: Min Capture a[(0, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:87:26
+ |
+LL | println!("{:?}", a.0);
+ | ^^^
+note: Min Capture a[(1, 0)] -> ImmBorrow
+ --> $DIR/preserve_field_drop_order.rs:84:26
+ |
+LL | println!("{:?}", a.1);
+ | ^^^
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs
new file mode 100644
index 000000000..1cae776dd
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs
@@ -0,0 +1,58 @@
+// run-pass
+// check-run-results
+// revisions: twenty_eighteen twenty_twentyone
+// [twenty_eighteen]compile-flags: --edition 2018
+// [twenty_twentyone]compile-flags: --edition 2021
+
+#[derive(Debug)]
+struct Dropable(&'static str);
+
+impl Drop for Dropable {
+ fn drop(&mut self) {
+ println!("Dropping {}", self.0)
+ }
+}
+
+#[derive(Debug)]
+struct A {
+ x: Dropable,
+ y: Dropable,
+}
+
+#[derive(Debug)]
+struct B {
+ c: A,
+ d: A,
+}
+
+#[derive(Debug)]
+struct R<'a> {
+ c: &'a A,
+ d: &'a A,
+}
+
+fn main() {
+ let a = A { x: Dropable("x"), y: Dropable("y") };
+
+ let c = move || println!("{:?} {:?}", a.y, a.x);
+
+ c();
+
+ let b = B {
+ c: A { x: Dropable("b.c.x"), y: Dropable("b.c.y") },
+ d: A { x: Dropable("b.d.x"), y: Dropable("b.d.y") },
+ };
+
+ let d = move || println!("{:?} {:?} {:?} {:?}", b.d.y, b.d.x, b.c.y, b.c.x);
+
+ d();
+
+ let r = R {
+ c: &A { x: Dropable("r.c.x"), y: Dropable("r.c.y") },
+ d: &A { x: Dropable("r.d.x"), y: Dropable("r.d.y") },
+ };
+
+ let e = move || println!("{:?} {:?} {:?} {:?}", r.d.y, r.d.x, r.c.y, r.c.x);
+
+ e();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout
new file mode 100644
index 000000000..557d047c1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout
@@ -0,0 +1,13 @@
+Dropable("y") Dropable("x")
+Dropable("b.d.y") Dropable("b.d.x") Dropable("b.c.y") Dropable("b.c.x")
+Dropable("r.d.y") Dropable("r.d.x") Dropable("r.c.y") Dropable("r.c.x")
+Dropping r.d.x
+Dropping r.d.y
+Dropping r.c.x
+Dropping r.c.y
+Dropping b.c.x
+Dropping b.c.y
+Dropping b.d.x
+Dropping b.d.y
+Dropping x
+Dropping y
diff --git a/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout
new file mode 100644
index 000000000..557d047c1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout
@@ -0,0 +1,13 @@
+Dropable("y") Dropable("x")
+Dropable("b.d.y") Dropable("b.d.x") Dropable("b.c.y") Dropable("b.c.x")
+Dropable("r.d.y") Dropable("r.d.x") Dropable("r.c.y") Dropable("r.c.x")
+Dropping r.d.x
+Dropping r.d.y
+Dropping r.c.x
+Dropping r.c.y
+Dropping b.c.x
+Dropping b.c.y
+Dropping b.d.x
+Dropping b.d.y
+Dropping x
+Dropping y
diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs
new file mode 100644
index 000000000..3ed780f51
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs
@@ -0,0 +1,101 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// `u8` aligned at a byte and are unaffected by repr(packed).
+// Therefore we can precisely (and safely) capture references to both the fields.
+fn test_alignment_not_affected() {
+ #[repr(packed)]
+ struct Foo { x: u8, y: u8 }
+
+ let mut foo = Foo { x: 0, y: 0 };
+
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let z1: &u8 = &foo.x;
+ //~^ NOTE: Capturing foo[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture foo[(0, 0)] -> ImmBorrow
+ let z2: &mut u8 = &mut foo.y;
+ //~^ NOTE: Capturing foo[(1, 0)] -> MutBorrow
+ //~| NOTE: Min Capture foo[(1, 0)] -> MutBorrow
+
+ *z2 = 42;
+
+ println!("({}, {})", z1, z2);
+ };
+
+ c();
+}
+
+// `String`, `u16` are not aligned at a one byte boundry and are thus affected by repr(packed).
+//
+// Here we test that the closure doesn't capture a reference point to `foo.x` but
+// rather capture `foo` entirely.
+fn test_alignment_affected() {
+ #[repr(packed)]
+ struct Foo { x: String, y: u16 }
+
+ let mut foo = Foo { x: String::new(), y: 0 };
+
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let z1: &String = &foo.x;
+ //~^ NOTE: Capturing foo[] -> ImmBorrow
+ let z2: &mut u16 = &mut foo.y;
+ //~^ NOTE: Capturing foo[] -> MutBorrow
+ //~| NOTE: Min Capture foo[] -> MutBorrow
+
+
+ *z2 = 42;
+
+ println!("({}, {})", z1, z2);
+ };
+
+ c();
+}
+
+// Given how the closure desugaring is implemented (at least at the time of writing this test),
+// we don't need to truncate the captured path to a reference into a packed-struct if the field
+// being referenced will be moved into the closure, since it's safe to move out a field from a
+// packed-struct.
+//
+// However to avoid surprises for the user, or issues when the closure is
+// inlined we will truncate the capture to access just the struct regardless of if the field
+// might get moved into the closure.
+fn test_truncation_when_ref_and_move() {
+ #[repr(packed)]
+ struct Foo { x: String }
+
+ let mut foo = Foo { x: String::new() };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{}", foo.x);
+ //~^ NOTE: Capturing foo[] -> ImmBorrow
+ //~| NOTE: Min Capture foo[] -> ByValue
+ //~| NOTE: foo[] used here
+ let _z = foo.x;
+ //~^ NOTE: Capturing foo[(0, 0)] -> ByValue
+ //~| NOTE: foo[] captured as ByValue here
+ };
+
+ c();
+}
+
+fn main() {
+ test_truncation_when_ref_and_move();
+ test_alignment_affected();
+ test_alignment_not_affected();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr
new file mode 100644
index 000000000..580061ebc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr
@@ -0,0 +1,161 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/repr_packed.rs:13:17
+ |
+LL | let mut 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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/repr_packed.rs:44:17
+ |
+LL | let mut 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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/repr_packed.rs:79: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
+
+error: First Pass analysis includes:
+ --> $DIR/repr_packed.rs:16:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let z1: &u8 = &foo.x;
+... |
+LL | | println!("({}, {})", z1, z2);
+LL | | };
+ | |_____^
+ |
+note: Capturing foo[(0, 0)] -> ImmBorrow
+ --> $DIR/repr_packed.rs:19:24
+ |
+LL | let z1: &u8 = &foo.x;
+ | ^^^^^
+note: Capturing foo[(1, 0)] -> MutBorrow
+ --> $DIR/repr_packed.rs:22:32
+ |
+LL | let z2: &mut u8 = &mut foo.y;
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/repr_packed.rs:16:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let z1: &u8 = &foo.x;
+... |
+LL | | println!("({}, {})", z1, z2);
+LL | | };
+ | |_____^
+ |
+note: Min Capture foo[(0, 0)] -> ImmBorrow
+ --> $DIR/repr_packed.rs:19:24
+ |
+LL | let z1: &u8 = &foo.x;
+ | ^^^^^
+note: Min Capture foo[(1, 0)] -> MutBorrow
+ --> $DIR/repr_packed.rs:22:32
+ |
+LL | let z2: &mut u8 = &mut foo.y;
+ | ^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/repr_packed.rs:47:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let z1: &String = &foo.x;
+... |
+LL | | println!("({}, {})", z1, z2);
+LL | | };
+ | |_____^
+ |
+note: Capturing foo[] -> ImmBorrow
+ --> $DIR/repr_packed.rs:50:28
+ |
+LL | let z1: &String = &foo.x;
+ | ^^^^^
+note: Capturing foo[] -> MutBorrow
+ --> $DIR/repr_packed.rs:52:33
+ |
+LL | let z2: &mut u16 = &mut foo.y;
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/repr_packed.rs:47:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let z1: &String = &foo.x;
+... |
+LL | | println!("({}, {})", z1, z2);
+LL | | };
+ | |_____^
+ |
+note: Min Capture foo[] -> MutBorrow
+ --> $DIR/repr_packed.rs:52:33
+ |
+LL | let z2: &mut u16 = &mut foo.y;
+ | ^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/repr_packed.rs:82:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", foo.x);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing foo[] -> ImmBorrow
+ --> $DIR/repr_packed.rs:85:24
+ |
+LL | println!("{}", foo.x);
+ | ^^^^^
+note: Capturing foo[(0, 0)] -> ByValue
+ --> $DIR/repr_packed.rs:89:18
+ |
+LL | let _z = foo.x;
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/repr_packed.rs:82:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | println!("{}", foo.x);
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture foo[] -> ByValue
+ --> $DIR/repr_packed.rs:85:24
+ |
+LL | println!("{}", foo.x);
+ | ^^^^^ foo[] used here
+...
+LL | let _z = foo.x;
+ | ^^^^^ foo[] captured as ByValue here
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs
new file mode 100644
index 000000000..73aca288f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/box.rs
@@ -0,0 +1,92 @@
+// edition:2021
+// run-pass
+
+// Test precise capture when using boxes
+
+struct MetaData { x: String, name: String }
+struct Data { m: MetaData }
+struct BoxedData(Box<Data>);
+struct EvenMoreBoxedData(Box<BoxedData>);
+
+// Mutate disjoint paths, one inside one outside the closure
+fn box_1() {
+ let m = MetaData { x: format!("x"), name: format!("name") };
+ let d = Data { m };
+ let b = BoxedData(Box::new(d));
+ let mut e = EvenMoreBoxedData(Box::new(b));
+
+ let mut c = || {
+ e.0.0.m.x = format!("not-x");
+ };
+
+ e.0.0.m.name = format!("not-name");
+ c();
+}
+
+// Mutate a path inside the closure and read a disjoint path outside the closure
+fn box_2() {
+ let m = MetaData { x: format!("x"), name: format!("name") };
+ let d = Data { m };
+ let b = BoxedData(Box::new(d));
+ let mut e = EvenMoreBoxedData(Box::new(b));
+
+ let mut c = || {
+ e.0.0.m.x = format!("not-x");
+ };
+
+ println!("{}", e.0.0.m.name);
+ c();
+}
+
+// Read a path inside the closure and mutate a disjoint path outside the closure
+fn box_3() {
+ let m = MetaData { x: format!("x"), name: format!("name") };
+ let d = Data { m };
+ let b = BoxedData(Box::new(d));
+ let mut e = EvenMoreBoxedData(Box::new(b));
+
+ let c = || {
+ println!("{}", e.0.0.m.name);
+ };
+
+ e.0.0.m.x = format!("not-x");
+ c();
+}
+
+// Read disjoint paths, one inside the closure and one outside the closure.
+fn box_4() {
+ let m = MetaData { x: format!("x"), name: format!("name") };
+ let d = Data { m };
+ let b = BoxedData(Box::new(d));
+ let e = EvenMoreBoxedData(Box::new(b));
+
+ let c = || {
+ println!("{}", e.0.0.m.name);
+ };
+
+ println!("{}", e.0.0.m.x);
+ c();
+}
+
+// Read the same path, once inside the closure and once outside the closure.
+fn box_5() {
+ let m = MetaData { x: format!("x"), name: format!("name") };
+ let d = Data { m };
+ let b = BoxedData(Box::new(d));
+ let e = EvenMoreBoxedData(Box::new(b));
+
+ let c = || {
+ println!("{}", e.0.0.m.name);
+ };
+
+ println!("{}", e.0.0.m.name);
+ c();
+}
+
+fn main() {
+ box_1();
+ box_2();
+ box_3();
+ box_4();
+ box_5();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs
new file mode 100644
index 000000000..2c828aed5
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs
@@ -0,0 +1,26 @@
+// edition:2021
+// run-pass
+
+// Test that ByValue captures compile sucessefully especially when the captures are
+// derefenced within the closure.
+
+#[derive(Debug, Default)]
+struct SomeLargeType;
+struct MuchLargerType([SomeLargeType; 32]);
+
+fn big_box() {
+ let s = MuchLargerType(Default::default());
+ let b = Box::new(s);
+ let t = (b, 10);
+
+ let c = || {
+ let p = t.0.0;
+ println!("{} {:?}", t.1, p);
+ };
+
+ c();
+}
+
+fn main() {
+ big_box();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs
new file mode 100644
index 000000000..3cb1eb329
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs
@@ -0,0 +1,24 @@
+// edition:2021
+// run-pass
+
+// Test that we can immutably borrow field of an instance of a structure from within a closure,
+// while having a mutable borrow to another field of the same instance outside the closure.
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let mut p = Point { x: 10, y: 10 };
+
+ let c = || {
+ println!("{}", p.x);
+ };
+
+ // `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
+ let py = &mut p.y;
+
+ c();
+ *py = 20;
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs
new file mode 100644
index 000000000..0f79b7ae7
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs
@@ -0,0 +1,20 @@
+// edition:2021
+// run-pass
+
+// Test that we can mutate an element of a tuple from within a closure
+// while immutably borrowing another element of the same tuple outside the closure.
+
+#![feature(rustc_attrs)]
+
+fn main() {
+ let mut t = (10, 10);
+
+ let mut c = || {
+ let t1 = &mut t.1;
+ *t1 = 20;
+ };
+
+ // Test that `c` only captures t.1, therefore reading t.0 is allowed.
+ println!("{}", t.0);
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs
new file mode 100644
index 000000000..81f0328b9
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs
@@ -0,0 +1,21 @@
+// edition:2021
+// run-pass
+
+// Test that we can immutably borrow an element of a tuple from within a closure,
+// while having a mutable borrow to another element of the same tuple outside the closure.
+
+#![feature(rustc_attrs)]
+
+fn main() {
+ let mut t = (10, 10);
+
+ let c = || {
+ println!("{}", t.0);
+ };
+
+ // `c` only captures t.0, therefore mutating t.1 is allowed.
+ let t1 = &mut t.1;
+
+ c();
+ *t1 = 20;
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs
new file mode 100644
index 000000000..cea02fbe1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs
@@ -0,0 +1,27 @@
+// edition:2021
+//check-pass
+
+fn test1() {
+ let foo : [Vec<u8>; 3] = ["String".into(), "String".into(), "String".into()];
+ let c = || {
+ match foo { _ => () };
+ };
+ drop(foo);
+ c();
+}
+
+fn test2() {
+ let foo : Option<[Vec<u8>; 3]> = Some(["String".into(), "String".into(), "String".into()]);
+ let c = || {
+ match foo {
+ Some(_) => 1,
+ _ => 2
+ };
+ };
+ c();
+}
+
+fn main() {
+ test1();
+ test2();
+}
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 000000000..5c278bff9
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs
@@ -0,0 +1,21 @@
+// edition:2021
+// check-pass
+#![warn(unused)]
+
+fn main() {
+ let t = (String::from("Hello"), String::from("World"));
+ let g = (String::from("Mr"), String::from("Goose"));
+
+ let a = || {
+ let (_, g2) = g;
+ //~^ WARN unused variable: `g2`
+ let c = || {
+ let (_, t2) = t;
+ //~^ WARN unused variable: `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 000000000..40274c883
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr
@@ -0,0 +1,21 @@
+warning: unused variable: `t2`
+ --> $DIR/destructure-pattern-closure-within-closure.rs:13:21
+ |
+LL | let (_, t2) = t;
+ | ^^ help: if this is intentional, prefix it with an underscore: `_t2`
+ |
+note: the lint level is defined here
+ --> $DIR/destructure-pattern-closure-within-closure.rs:3:9
+ |
+LL | #![warn(unused)]
+ | ^^^^^^
+ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+
+warning: unused variable: `g2`
+ --> $DIR/destructure-pattern-closure-within-closure.rs:10:17
+ |
+LL | let (_, g2) = g;
+ | ^^ help: if this is intentional, prefix it with an underscore: `_g2`
+
+warning: 2 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 000000000..dacc2c616
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs
@@ -0,0 +1,119 @@
+// edition:2021
+// check-pass
+#![warn(unused)]
+
+struct Point {
+ x: u32,
+ y: u32,
+}
+
+fn test1() {
+ let t = (String::from("Hello"), String::from("World"));
+
+ let c = || {
+ let (t1, t2) = t;
+ //~^ WARN unused variable: `t1`
+ //~| WARN unused variable: `t2`
+ };
+
+ c();
+}
+
+fn test2() {
+ let t = (String::from("Hello"), String::from("World"));
+
+ let c = || {
+ let (t1, _) = t;
+ //~^ WARN unused variable: `t1`
+ };
+
+ c();
+}
+
+fn test3() {
+ let t = (String::from("Hello"), String::from("World"));
+
+ let c = || {
+ let (_, t2) = t;
+ //~^ WARN unused variable: `t2`
+ };
+
+ c();
+}
+
+fn test4() {
+ let t = (String::from("Hello"), String::from("World"));
+
+ let c = || {
+ let (_, _) = t;
+ };
+
+ c();
+}
+
+fn test5() {
+ let t = (String::new(), String::new());
+ let _c = || {
+ let _a = match t {
+ (t1, _) => t1,
+ };
+ };
+}
+
+fn test6() {
+ let t = (String::new(), String::new());
+ let _c = || {
+ let _a = match t {
+ (_, t2) => t2,
+ };
+ };
+}
+
+fn test7() {
+ let t = (String::new(), String::new());
+ let _c = || {
+ let _a = match t {
+ (t1, t2) => (t1, t2),
+ };
+ };
+}
+
+fn test8() {
+ let x = 0;
+ let tup = (1, 2);
+ let p = Point { x: 10, y: 20 };
+
+ let c = || {
+ let _ = x;
+ let Point { x, y } = p;
+ //~^ WARN unused variable: `x`
+ println!("{}", y);
+ let (_, _) = tup;
+ };
+
+ c();
+}
+
+fn test9() {
+ 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();
+ test9();
+}
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 000000000..7706f68ba
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr
@@ -0,0 +1,39 @@
+warning: unused variable: `t1`
+ --> $DIR/destructure_patterns.rs:14:14
+ |
+LL | let (t1, t2) = t;
+ | ^^ help: if this is intentional, prefix it with an underscore: `_t1`
+ |
+note: the lint level is defined here
+ --> $DIR/destructure_patterns.rs:3:9
+ |
+LL | #![warn(unused)]
+ | ^^^^^^
+ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+
+warning: unused variable: `t2`
+ --> $DIR/destructure_patterns.rs:14:18
+ |
+LL | let (t1, t2) = t;
+ | ^^ help: if this is intentional, prefix it with an underscore: `_t2`
+
+warning: unused variable: `t1`
+ --> $DIR/destructure_patterns.rs:26:14
+ |
+LL | let (t1, _) = t;
+ | ^^ help: if this is intentional, prefix it with an underscore: `_t1`
+
+warning: unused variable: `t2`
+ --> $DIR/destructure_patterns.rs:37:17
+ |
+LL | let (_, t2) = t;
+ | ^^ help: if this is intentional, prefix it with an underscore: `_t2`
+
+warning: unused variable: `x`
+ --> $DIR/destructure_patterns.rs:88:21
+ |
+LL | let Point { x, y } = p;
+ | ^ help: try ignoring the field: `x: _`
+
+warning: 5 warnings emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs
new file mode 100644
index 000000000..88a9816a0
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs
@@ -0,0 +1,23 @@
+// edition:2021
+// run-pass
+
+// Tests that if a closure uses indivual fields of the same object
+// then that case is handled properly.
+
+#![allow(unused)]
+
+struct Struct {
+ x: i32,
+ y: i32,
+ s: String,
+}
+
+fn main() {
+ let mut s = Struct { x: 10, y: 10, s: String::new() };
+
+ let mut c = {
+ s.x += 10;
+ s.y += 42;
+ s.s = String::from("new");
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs
new file mode 100644
index 000000000..477fdd613
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs
@@ -0,0 +1,11 @@
+// edition:2021
+// check-pass
+#![feature(rustc_attrs)]
+
+fn main() {
+ let mut x = 1;
+ let c = || {
+ drop(&mut x);
+ match x { _ => () }
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs
new file mode 100644
index 000000000..20bbe1d89
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs
@@ -0,0 +1,23 @@
+// edition:2021
+// run-pass
+
+// Test that edition 2021 enables disjoint capture by default.
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let mut p = Point { x: 10, y: 10 };
+
+ let c = || {
+ println!("{}", p.x);
+ };
+
+ // `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
+ let py = &mut p.y;
+
+ c();
+ *py = 20;
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs
new file mode 100644
index 000000000..e19f5ff1b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs
@@ -0,0 +1,37 @@
+// edition:2021
+// run-pass
+
+// Test disjoint capture within an impl block
+
+struct Filter {
+ div: i32,
+}
+impl Filter {
+ fn allowed(&self, x: i32) -> bool {
+ x % self.div == 1
+ }
+}
+
+struct Data {
+ filter: Filter,
+ list: Vec<i32>,
+}
+impl Data {
+ fn update(&mut self) {
+ // The closure passed to filter only captures self.filter,
+ // therefore mutating self.list is allowed.
+ self.list.retain(
+ |v| self.filter.allowed(*v),
+ );
+ }
+}
+
+fn main() {
+ let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() };
+
+ for i in 1..10 {
+ d.list.push(i);
+ }
+
+ d.update();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs
new file mode 100644
index 000000000..1286613cb
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs
@@ -0,0 +1,42 @@
+// edition:2021
+// run-pass
+
+// Test that functional record update/struct update syntax works inside
+// a closure when the feature `capture_disjoint_fields` is enabled.
+
+#[derive(Clone)]
+struct S {
+ a: String,
+ b: String,
+}
+
+struct T {
+ a: String,
+ s: S,
+}
+
+fn main() {
+ let a = String::new();
+ let b = String::new();
+ let c = String::new();
+ let s = S {a, b};
+ let t = T {
+ a: c,
+ s: s.clone()
+ };
+
+ let c = || {
+ let s2 = S {
+ a: format!("New s2"),
+ ..s
+ };
+ let s3 = S {
+ a: format!("New s3"),
+ ..t.s
+ };
+ println!("{} {}", s2.a, s2.b);
+ println!("{} {} {}", s3.a, s3.b, t.a);
+ };
+
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs
new file mode 100644
index 000000000..c64475fda
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs
@@ -0,0 +1,16 @@
+// edition:2021
+// check-pass
+
+union Union {
+ value: u64,
+}
+
+fn main() {
+ let u = Union { value: 42 };
+
+ let c = || {
+ unsafe { u.value }
+ };
+
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs
new file mode 100644
index 000000000..25fbb6cb9
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs
@@ -0,0 +1,19 @@
+// edition:2021
+// run-pass
+
+
+fn solve<F>(validate: F) -> Option<u64>
+where
+ F: Fn(&mut [i8; 1]),
+{
+ let mut position: [i8; 1] = [1];
+ Some(0).map(|_| {
+ validate(&mut position);
+ let [_x] = position;
+ 0
+ })
+}
+
+fn main() {
+ solve(|_| ());
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs
new file mode 100644
index 000000000..999620530
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs
@@ -0,0 +1,59 @@
+// edition:2021
+// check-pass
+
+use std::collections::HashMap;
+use std::future::Future;
+use std::pin::Pin;
+
+pub struct GameMode {}
+
+struct GameStateManager<'a> {
+ gamestate_stack: Vec<Box<dyn GameState<'a> + 'a>>,
+}
+
+pub trait GameState<'a> {}
+
+async fn construct_gamestate_replay<'a>(
+ _gamemode: &GameMode,
+ _factory: &mut GameStateManager<'a>,
+) -> Box<dyn GameState<'a> + 'a> {
+ unimplemented!()
+}
+
+type FutureGameState<'a, 'b> = Pin<Box<dyn Future<Output = Box<dyn GameState<'a> + 'a>> + 'b>>;
+
+struct MenuOption<'a> {
+ command: Box<dyn for<'b> Fn(&'b mut GameStateManager<'a>) -> FutureGameState<'a, 'b> + 'a>,
+}
+
+impl<'a> MenuOption<'a> {
+ fn new(
+ _command: impl for<'b> Fn(&'b mut GameStateManager<'a>) -> FutureGameState<'a, 'b> + 'a,
+ ) -> Self {
+ unimplemented!()
+ }
+}
+
+struct MenuState<'a> {
+ options: Vec<MenuOption<'a>>,
+}
+
+impl<'a> GameState<'a> for MenuState<'a> {}
+
+pub async fn get_replay_menu<'a>(
+ gamemodes: &'a HashMap<&str, GameMode>,
+) -> Box<dyn GameState<'a> + 'a> {
+ let recordings: Vec<String> = vec![];
+ let _ = recordings
+ .into_iter()
+ .map(|entry| {
+ MenuOption::new(move |f| {
+ Box::pin(construct_gamestate_replay(&gamemodes[entry.as_str()], f))
+ })
+ })
+ .collect::<Vec<_>>();
+
+ todo!()
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs
new file mode 100644
index 000000000..f44c2af80
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs
@@ -0,0 +1,47 @@
+// check-pass
+// edition:2021
+
+use std::rc::Rc;
+
+// Test that we restrict precision when moving not-`Copy` types, if any of the parent paths
+// implement `Drop`. This is to ensure that we don't move out of a type that implements Drop.
+pub fn test1() {
+ struct Foo(Rc<i32>);
+
+ impl Drop for Foo {
+ fn drop(self: &mut Foo) {}
+ }
+
+ let f = Foo(Rc::new(1));
+ let x = move || {
+ println!("{:?}", f.0);
+ };
+
+ x();
+}
+
+
+// Test that we don't restrict precision when moving `Copy` types(i.e. when copying),
+// even if any of the parent paths implement `Drop`.
+pub fn test2() {
+ struct Character {
+ hp: u32,
+ name: String,
+ }
+
+ impl Drop for Character {
+ fn drop(&mut self) {}
+ }
+
+ let character = Character { hp: 100, name: format!("A") };
+
+ let c = move || {
+ println!("{}", character.hp)
+ };
+
+ c();
+
+ println!("{}", character.name);
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs
new file mode 100644
index 000000000..d2375aa69
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs
@@ -0,0 +1,30 @@
+// edition:2021
+//check-pass
+#![warn(unused)]
+#![feature(rustc_attrs)]
+#![feature(btree_drain_filter)]
+
+use std::collections::BTreeMap;
+use std::panic::{catch_unwind, AssertUnwindSafe};
+
+fn main() {
+ let mut map = BTreeMap::new();
+ map.insert("a", ());
+ map.insert("b", ());
+ map.insert("c", ());
+
+ {
+ let mut it = map.drain_filter(|_, _| true);
+ catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
+ let result = catch_unwind(AssertUnwindSafe(|| it.next()));
+ assert!(matches!(result, Ok(None)));
+ }
+
+ {
+ let mut it = map.drain_filter(|_, _| true);
+ catch_unwind(AssertUnwindSafe(|| while let Some(_) = it.next() {})).unwrap_err();
+ let result = catch_unwind(AssertUnwindSafe(|| it.next()));
+ assert!(matches!(result, Ok(None)));
+ }
+
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
new file mode 100644
index 000000000..f76965bdd
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
@@ -0,0 +1,93 @@
+// edition:2021
+// run-pass
+
+// Test that move closures compile properly with `capture_disjoint_fields` enabled.
+
+#![allow(unused)]
+
+fn simple_ref() {
+ let mut s = 10;
+ let ref_s = &mut s;
+
+ let mut c = move || {
+ *ref_s += 10;
+ };
+ c();
+}
+
+fn struct_contains_ref_to_another_struct() {
+ struct S(String);
+ struct T<'a>(&'a mut S);
+
+ let mut s = S("s".into());
+ let t = T(&mut s);
+
+ let mut c = move || {
+ t.0.0 = "new s".into();
+ };
+
+ c();
+}
+
+#[derive(Debug)]
+struct S(String);
+
+#[derive(Debug)]
+struct T(S);
+
+fn no_ref() {
+ let mut t = T(S("s".into()));
+ let mut c = move || {
+ t.0.0 = "new S".into();
+ };
+ c();
+}
+
+fn no_ref_nested() {
+ let mut t = T(S("s".into()));
+ let c = || {
+ println!("{:?}", t.0);
+ let mut c = move || {
+ t.0.0 = "new S".into();
+ println!("{:?}", t.0.0);
+ };
+ c();
+ };
+ c();
+}
+
+// Test that even if a path is moved into the closure, the closure is not FnOnce
+// if the path is not moved by the closure call.
+fn data_moved_but_not_fn_once() {
+ let x = Box::new(10i32);
+
+ let c = move || {
+ // *x has type i32 which is Copy. So even though the box `x` will be moved
+ // into the closure, `x` is never moved when the closure is called, i.e. the
+ // ownership stays with the closure and therefore we can call the function multiple times.
+ let _x = *x;
+ };
+
+ c();
+ c();
+}
+
+// Test that move closures can take ownership of Copy type
+fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
+ let x = 10;
+
+ let c = move || x;
+
+ c
+}
+
+fn main() {
+ simple_ref();
+ struct_contains_ref_to_another_struct();
+ no_ref();
+ no_ref_nested();
+
+ data_moved_but_not_fn_once();
+
+ returned_closure_owns_copy_type_data();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs
new file mode 100644
index 000000000..b8e464031
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs
@@ -0,0 +1,33 @@
+// edition:2021
+// run-pass
+
+// Test that closures can catpure paths that are more precise than just one level
+// from the root variable.
+//
+// If the closures can handle such precison we should be able to mutate one path in the closure
+// while being able to mutate another path outside the closure, where the two paths are disjoint
+// after applying two projections on the root variable.
+
+#![allow(unused)]
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+struct Wrapper {
+ p: Point,
+}
+
+fn main() {
+ let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+ let mut c = || {
+ w.p.x += 20;
+ };
+
+ // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`.
+ let py = &mut w.p.y;
+ c();
+
+ *py = 20
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs
new file mode 100644
index 000000000..11a324d8a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs
@@ -0,0 +1,31 @@
+// edition:2021
+// run-pass
+
+#![allow(unused)]
+
+// If the closures can handle such precison we should be able to read one path in the closure
+// while being able mutate another path outside the closure, where the two paths are disjoint
+// after applying two projections on the root variable.
+
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+struct Wrapper {
+ p: Point,
+}
+
+fn main() {
+ let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+ let c = || {
+ println!("{}", w.p.x);
+ };
+
+ // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`.
+ let py = &mut w.p.y;
+ c();
+
+ *py = 20
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs
new file mode 100644
index 000000000..8fc0efb60
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs
@@ -0,0 +1,28 @@
+// edition:2021
+// run-pass
+
+#![allow(unused)]
+
+// Test that when `capture_disjoint_fields` is enabled we can read a path
+// both inside and outside the closure at the same time.
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+struct Wrapper {
+ p: Point,
+}
+
+fn main() {
+ let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+ let c = || {
+ println!("{}", w.p.x);
+ };
+
+ let px = &w.p.x;
+ c();
+
+ println!("{}", px);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs
new file mode 100644
index 000000000..9f0c4d96a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs
@@ -0,0 +1,54 @@
+// edition:2021
+// run-pass
+
+// Test that we can mutate a place through a mut-borrow
+// that is captured by the closure
+
+// Check that we can mutate when one deref is required
+fn mut_ref_1() {
+ let mut x = String::new();
+ let rx = &mut x;
+
+ let mut c = || {
+ *rx = String::new();
+ };
+
+ c();
+}
+
+// Similar example as mut_ref_1, we don't deref the imm-borrow here,
+// and so we are allowed to mutate.
+fn mut_ref_2() {
+ let x = String::new();
+ let y = String::new();
+ let mut ref_x = &x;
+ let m_ref_x = &mut ref_x;
+
+ let mut c = || {
+ *m_ref_x = &y;
+ };
+
+ c();
+}
+
+// Check that we can mutate when multiple derefs of mut-borrows are required to reach
+// the target place.
+// It works because all derefs are mutable, if either of them was an immutable
+// borrow, then we would not be able to deref.
+fn mut_mut_ref() {
+ let mut x = String::new();
+ let mut mref_x = &mut x;
+ let m_mref_x = &mut mref_x;
+
+ let mut c = || {
+ **m_mref_x = String::new();
+ };
+
+ c();
+}
+
+fn main() {
+ mut_ref_1();
+ mut_ref_2();
+ mut_mut_ref();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
new file mode 100644
index 000000000..bb784774b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
@@ -0,0 +1,43 @@
+// edition:2021
+// run-pass
+
+// Test that we can mutate a place through a mut-borrow
+// that is captured by the closure
+
+// More specifically we test that the if the mutable reference isn't root variable of a capture
+// but rather accessed while acessing the precise capture.
+
+fn mut_tuple() {
+ let mut t = (10, 10);
+
+ let t1 = (&mut t, 10);
+
+ let mut c = || {
+ // Mutable because (*t.0) is mutable
+ t1.0.0 += 10;
+ };
+
+ c();
+}
+
+fn mut_tuple_nested() {
+ let mut t = (10, 10);
+
+ let t1 = (&mut t, 10);
+
+ let mut c = || {
+ let mut c = || {
+ // Mutable because (*t.0) is mutable
+ t1.0.0 += 10;
+ };
+
+ c();
+ };
+
+ c();
+}
+
+fn main() {
+ mut_tuple();
+ mut_tuple_nested();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs
new file mode 100644
index 000000000..a80b40bb4
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs
@@ -0,0 +1,36 @@
+// edition:2021
+// run-pass
+
+// Test whether if we can do precise capture when using nested clsoure.
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let mut p = Point { x: 5, y: 20 };
+
+ // c1 should capture `p.x` via immutable borrow and
+ // `p.y` via mutable borrow.
+ let mut c1 = || {
+ println!("{}", p.x);
+
+ let incr = 10;
+
+ let mut c2 = || p.y += incr;
+ c2();
+
+ println!("{}", p.y);
+ };
+
+ c1();
+
+ // This should not throw an error because `p.x` is borrowed via Immutable borrow,
+ // and multiple immutable borrow of the same place are allowed.
+ let px = &p.x;
+
+ println!("{}", px);
+
+ c1();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs
new file mode 100644
index 000000000..ed222b314
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs
@@ -0,0 +1,49 @@
+// edition:2021
+//check-pass
+#![warn(unused)]
+#![allow(dead_code)]
+#![feature(rustc_attrs)]
+
+#[derive(Debug, Clone, Copy)]
+enum PointType {
+ TwoD { x: u32, y: u32 },
+
+ ThreeD{ x: u32, y: u32, z: u32 }
+}
+
+// Testing struct patterns
+struct Points {
+ points: Vec<PointType>,
+}
+
+impl Points {
+ pub fn test1(&mut self) -> Vec<usize> {
+ (0..self.points.len())
+ .filter_map(|i| {
+ let idx = i as usize;
+ match self.test2(idx) {
+ PointType::TwoD { .. } => Some(i),
+ PointType::ThreeD { .. } => None,
+ }
+ })
+ .collect()
+ }
+
+ pub fn test2(&mut self, i: usize) -> PointType {
+ self.points[i]
+ }
+}
+
+fn main() {
+ let mut points = Points {
+ points: Vec::<PointType>::new()
+ };
+
+ points.points.push(PointType::ThreeD { x:0, y:0, z:0 });
+ points.points.push(PointType::TwoD{ x:0, y:0 });
+ points.points.push(PointType::ThreeD{ x:0, y:0, z:0 });
+ points.points.push(PointType::TwoD{ x:0, y:0 });
+
+ println!("{:?}", points.test1());
+ println!("{:?}", points.points);
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs
new file mode 100644
index 000000000..f3f44433c
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs
@@ -0,0 +1,43 @@
+// edition:2021
+//check-pass
+
+#[derive(Copy, Clone)]
+enum PointType {
+ TwoD(u32, u32),
+ ThreeD(u32, u32, u32)
+}
+
+// Testing tuple struct patterns
+struct Points {
+ points: Vec<PointType>,
+}
+
+impl Points {
+ pub fn test1(&mut self) -> Vec<usize> {
+ (0..self.points.len())
+ .filter_map(|i| {
+ match self.test2(i) {
+ PointType::TwoD (..) => Some(i),
+ PointType::ThreeD (..) => None,
+ }
+ })
+ .collect()
+ }
+
+ pub fn test2(&mut self, i: usize) -> PointType {
+ self.points[i]
+ }
+}
+
+fn main() {
+ let mut points = Points {
+ points: Vec::<PointType>::new()
+ };
+
+ points.points.push(PointType::ThreeD(0,0,0));
+ points.points.push(PointType::TwoD(0,0));
+ points.points.push(PointType::ThreeD(0,0,1));
+ points.points.push(PointType::TwoD(0,1));
+
+ println!("{:?}", points.test1());
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs
new file mode 100644
index 000000000..3f7ddf93f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs
@@ -0,0 +1,47 @@
+// edition:2021
+// run-pass
+
+// Test that we can use raw ptrs when using `capture_disjoint_fields`.
+
+#![allow(dead_code)]
+
+#[derive(Debug)]
+struct S {
+ s: String,
+ t: String,
+}
+
+struct T(*const S);
+
+fn unsafe_imm() {
+ let s = "".into();
+ let t = "".into();
+ let my_speed: Box<S> = Box::new(S { s, t });
+
+ let p : *const S = Box::into_raw(my_speed);
+ let t = T(p);
+
+ let c = || unsafe {
+ println!("{:?}", (*t.0).s);
+ };
+
+ c();
+}
+
+fn unsafe_mut() {
+ let s = "".into();
+ let t = "".into();
+ let mut my_speed: Box<S> = Box::new(S { s, t });
+ let p : *mut S = &mut *my_speed;
+
+ let c = || {
+ let x = unsafe { &mut (*p).s };
+ *x = "s".into();
+ };
+ c();
+}
+
+fn main() {
+ unsafe_mut();
+ unsafe_imm();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs
new file mode 100644
index 000000000..0206927cc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs
@@ -0,0 +1,11 @@
+// edition:2021
+//check-pass
+#![feature(rustc_attrs)]
+
+fn main() {
+ let mut x = 0;
+ let c = || {
+ &mut x; // mutable borrow of `x`
+ match x { _ => () } // fake read of `x`
+ };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs
new file mode 100644
index 000000000..563095d44
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs
@@ -0,0 +1,39 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test to ensure that min analysis meets capture kind for all paths captured.
+
+#[derive(Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let mut p = Point { x: 10, y: 20 };
+
+ //
+ // Requirements:
+ // p.x -> MutBoorrow
+ // p -> ImmBorrow
+ //
+ // Requirements met when p is captured via MutBorrow
+ //
+ let mut c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ p.x += 10;
+ //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow
+ //~| NOTE: p[] captured as MutBorrow here
+ println!("{:?}", p);
+ //~^ NOTE: Capturing p[] -> ImmBorrow
+ //~| NOTE: Min Capture p[] -> MutBorrow
+ //~| NOTE: p[] used here
+ };
+
+ c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr
new file mode 100644
index 000000000..05d79797a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr
@@ -0,0 +1,56 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/simple-struct-min-capture.rs:23:17
+ |
+LL | let mut 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
+
+error: First Pass analysis includes:
+ --> $DIR/simple-struct-min-capture.rs:26:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | p.x += 10;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing p[(0, 0)] -> MutBorrow
+ --> $DIR/simple-struct-min-capture.rs:29:9
+ |
+LL | p.x += 10;
+ | ^^^
+note: Capturing p[] -> ImmBorrow
+ --> $DIR/simple-struct-min-capture.rs:32:26
+ |
+LL | println!("{:?}", p);
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/simple-struct-min-capture.rs:26:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | p.x += 10;
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[] -> MutBorrow
+ --> $DIR/simple-struct-min-capture.rs:29:9
+ |
+LL | p.x += 10;
+ | ^^^ p[] captured as MutBorrow here
+...
+LL | println!("{:?}", p);
+ | ^ p[] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs
new file mode 100644
index 000000000..eab9f9d08
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs
@@ -0,0 +1,62 @@
+// edition:2021
+
+// Test that we restrict precision of a capture when we access a raw ptr,
+// i.e. the capture doesn't deref the raw ptr.
+
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct S {
+ s: String,
+ t: String,
+}
+
+struct T(*const S);
+
+fn unsafe_imm() {
+ let s = "".into();
+ let t = "".into();
+ let my_speed: Box<S> = Box::new(S { s, t });
+
+ let p : *const S = Box::into_raw(my_speed);
+ let t = T(p);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || unsafe {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ println!("{:?}", (*t.0).s);
+ //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow
+ };
+
+ c();
+}
+
+fn unsafe_mut() {
+ let s = "".into();
+ let t = "".into();
+ let mut my_speed: Box<S> = Box::new(S { s, t });
+ let p : *mut S = &mut *my_speed;
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ let x = unsafe { &mut (*p).s };
+ //~^ NOTE: Capturing p[Deref,(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture p[] -> ImmBorrow
+ *x = "s".into();
+ };
+ c();
+}
+
+fn main() {
+ unsafe_mut();
+ unsafe_imm();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr
new file mode 100644
index 000000000..e740a4d2d
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr
@@ -0,0 +1,93 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/unsafe_ptr.rs:25: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/unsafe_ptr.rs:45: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
+
+error: First Pass analysis includes:
+ --> $DIR/unsafe_ptr.rs:28:6
+ |
+LL | / || unsafe {
+LL | |
+LL | |
+LL | | println!("{:?}", (*t.0).s);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/unsafe_ptr.rs:31:26
+ |
+LL | println!("{:?}", (*t.0).s);
+ | ^^^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/unsafe_ptr.rs:28:6
+ |
+LL | / || unsafe {
+LL | |
+LL | |
+LL | | println!("{:?}", (*t.0).s);
+LL | |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0)] -> ImmBorrow
+ --> $DIR/unsafe_ptr.rs:31:26
+ |
+LL | println!("{:?}", (*t.0).s);
+ | ^^^^^^^^
+
+error: First Pass analysis includes:
+ --> $DIR/unsafe_ptr.rs:48:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let x = unsafe { &mut (*p).s };
+... |
+LL | | *x = "s".into();
+LL | | };
+ | |_____^
+ |
+note: Capturing p[Deref,(0, 0)] -> ImmBorrow
+ --> $DIR/unsafe_ptr.rs:51:31
+ |
+LL | let x = unsafe { &mut (*p).s };
+ | ^^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/unsafe_ptr.rs:48:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | let x = unsafe { &mut (*p).s };
+... |
+LL | | *x = "s".into();
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[] -> ImmBorrow
+ --> $DIR/unsafe_ptr.rs:51:31
+ |
+LL | let x = unsafe { &mut (*p).s };
+ | ^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs
new file mode 100644
index 000000000..a795088a1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs
@@ -0,0 +1,73 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test to ensure that we can handle cases where
+// let statements create no bindings are initialized
+// using a Place expression
+//
+// Note: Currently when feature `capture_disjoint_fields` is enabled
+// we can't handle such cases. So the test current use `_x` instead of
+// `_` until the issue is resolved.
+// Check rust-lang/project-rfc-2229#24 for status.
+
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn wild_struct() {
+ let p = Point { x: 10, y: 20 };
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ // FIXME(arora-aman): Change `_x` to `_`
+ let Point { x: _x, y: _ } = p;
+ //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+ //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
+ };
+
+ c();
+}
+
+fn wild_tuple() {
+ let t = (String::new(), 10);
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ // FIXME(arora-aman): Change `_x` to `_`
+ let (_x, _) = t;
+ //~^ NOTE: Capturing t[(0, 0)] -> ByValue
+ //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+ };
+
+ c();
+}
+
+fn wild_arr() {
+ let arr = [String::new(), String::new()];
+
+ let c = #[rustc_capture_analysis]
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ || {
+ //~^ ERROR: First Pass analysis includes:
+ //~| ERROR: Min Capture analysis includes:
+ // FIXME(arora-aman): Change `_x` to `_`
+ let [_x, _] = arr;
+ //~^ NOTE: Capturing arr[Index] -> ByValue
+ //~| NOTE: Min Capture arr[] -> ByValue
+ };
+
+ c();
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr
new file mode 100644
index 000000000..c64378091
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr
@@ -0,0 +1,138 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/wild_patterns.rs:22: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/wild_patterns.rs:40: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
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/wild_patterns.rs:58: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
+
+error: First Pass analysis includes:
+ --> $DIR/wild_patterns.rs:25:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | // FIXME(arora-aman): Change `_x` to `_`
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing p[(0, 0)] -> ImmBorrow
+ --> $DIR/wild_patterns.rs:29:37
+ |
+LL | let Point { x: _x, y: _ } = p;
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/wild_patterns.rs:25:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | // FIXME(arora-aman): Change `_x` to `_`
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture p[(0, 0)] -> ImmBorrow
+ --> $DIR/wild_patterns.rs:29:37
+ |
+LL | let Point { x: _x, y: _ } = p;
+ | ^
+
+error: First Pass analysis includes:
+ --> $DIR/wild_patterns.rs:43:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | // FIXME(arora-aman): Change `_x` to `_`
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing t[(0, 0)] -> ByValue
+ --> $DIR/wild_patterns.rs:47:23
+ |
+LL | let (_x, _) = t;
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/wild_patterns.rs:43:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | // FIXME(arora-aman): Change `_x` to `_`
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture t[(0, 0)] -> ByValue
+ --> $DIR/wild_patterns.rs:47:23
+ |
+LL | let (_x, _) = t;
+ | ^
+
+error: First Pass analysis includes:
+ --> $DIR/wild_patterns.rs:61:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | // FIXME(arora-aman): Change `_x` to `_`
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Capturing arr[Index] -> ByValue
+ --> $DIR/wild_patterns.rs:65:23
+ |
+LL | let [_x, _] = arr;
+ | ^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/wild_patterns.rs:61:5
+ |
+LL | / || {
+LL | |
+LL | |
+LL | | // FIXME(arora-aman): Change `_x` to `_`
+... |
+LL | |
+LL | | };
+ | |_____^
+ |
+note: Min Capture arr[] -> ByValue
+ --> $DIR/wild_patterns.rs:65:23
+ |
+LL | let [_x, _] = arr;
+ | ^^^
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0658`.