Skip to content

Commit 85dbb03

Browse files
authored
Rollup merge of #76119 - Amjad50:stabilizing-move_ref_pattern, r=nikomatsakis
Stabilize move_ref_pattern # Implementation - Initially the rule was added in the run-up to 1.0. The AST-based borrow checker was having difficulty correctly enforcing match expressions that combined ref and move bindings, and so it was decided to simplify forbid the combination out right. - The move to MIR-based borrow checking made it possible to enforce the rules in a finer-grained level, but we kept the rule in place in an effort to be conservative in our changes. - In #68376, @Centril lifted the restriction but required a feature-gate. - This PR removes the feature-gate. Tracking issue: #68354. # Description This PR is to stabilize the feature `move_ref_pattern`, which allows patterns containing both `by-ref` and `by-move` bindings at the same time. For example: `Foo(ref x, y)`, where `x` is `by-ref`, and `y` is `by-move`. The rules of moving a variable also apply here when moving *part* of a variable, such as it can't be referenced or moved before. If this pattern is used, it would result in *partial move*, which means that part of the variable is moved. The variable that was partially moved from cannot be used as a whole in this case, only the parts that are still not moved can be used. ## Documentation - The reference (rust-lang/reference#881) - Rust by example (rust-lang/rust-by-example#1377) ## Tests There are many tests, but I think one of the comperhensive ones: - [borrowck-move-ref-pattern-pass.rs](https://github.com/Centril/rust/blob/85fbf49ce0e2274d0acf798f6e703747674feec3/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs) - [borrowck-move-ref-pattern.rs](https://github.com/Centril/rust/blob/85fbf49ce0e2274d0acf798f6e703747674feec3/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.rs) # Examples ```rust #[derive(PartialEq, Eq)] struct Finished {} #[derive(PartialEq, Eq)] struct Processing { status: ProcessStatus, } #[derive(PartialEq, Eq)] enum ProcessStatus { One, Two, Three, } #[derive(PartialEq, Eq)] enum Status { Finished(Finished), Processing(Processing), } fn check_result(_url: &str) -> Status { // fetch status from some server Status::Processing(Processing { status: ProcessStatus::One, }) } fn wait_for_result(url: &str) -> Finished { let mut previous_status = None; loop { match check_result(url) { Status::Finished(f) => return f, Status::Processing(p) => { match (&mut previous_status, p.status) { (None, status) => previous_status = Some(status), // first status (Some(previous), status) if *previous == status => {} // no change, ignore (Some(previous), status) => { // Now it can be used // new status *previous = status; } } } } } } ``` Before, we would have used: ```rust match (&previous_status, p.status) { (Some(previous), status) if *previous == status => {} // no change, ignore (_, status) => { // new status previous_status = Some(status); } } ``` Demonstrating *partial move* ```rust fn main() { #[derive(Debug)] struct Person { name: String, age: u8, } let person = Person { name: String::from("Alice"), age: 20, }; // `name` is moved out of person, but `age` is referenced let Person { name, ref age } = person; println!("The person's age is {}", age); println!("The person's name is {}", name); // Error! borrow of partially moved value: `person` partial move occurs //println!("The person struct is {:?}", person); // `person` cannot be used but `person.age` can be used as it is not moved println!("The person's age from person struct is {}", person.age); } ```
2 parents 5acb7f1 + afb9eeb commit 85dbb03

File tree

50 files changed

+277
-494
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+277
-494
lines changed

compiler/rustc_error_codes/src/error_codes/E0007.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#### Note: this error code is no longer emitted by the compiler.
2+
13
This error indicates that the bindings in a match arm would require a value to
24
be moved into more than one location, thus violating unique ownership. Code
35
like the following is invalid as it requires the entire `Option<String>` to be
@@ -6,11 +8,13 @@ inner `String` to be moved into a variable called `s`.
68

79
Erroneous code example:
810

9-
```compile_fail,E0007
11+
```compile_fail,E0382
12+
#![feature(bindings_after_at)]
13+
1014
let x = Some("s".to_string());
1115
1216
match x {
13-
op_string @ Some(s) => {}, // error: cannot bind by-move with sub-bindings
17+
op_string @ Some(s) => {}, // error: use of moved value
1418
None => {},
1519
}
1620
```

compiler/rustc_feature/src/accepted.rs

+3
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ declare_features! (
270270
(accepted, track_caller, "1.46.0", Some(47809), None),
271271
/// Allows `#[doc(alias = "...")]`.
272272
(accepted, doc_alias, "1.48.0", Some(50146), None),
273+
/// Allows patterns with concurrent by-move and by-ref bindings.
274+
/// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref.
275+
(accepted, move_ref_pattern, "1.48.0", Some(68354), None),
273276

274277
// -------------------------------------------------------------------------
275278
// feature-group-end: accepted features

compiler/rustc_feature/src/active.rs

-4
Original file line numberDiff line numberDiff line change
@@ -526,10 +526,6 @@ declare_features! (
526526
/// For example, you can write `x @ Some(y)`.
527527
(active, bindings_after_at, "1.41.0", Some(65490), None),
528528

529-
/// Allows patterns with concurrent by-move and by-ref bindings.
530-
/// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref.
531-
(active, move_ref_pattern, "1.42.0", Some(68354), None),
532-
533529
/// Allows `impl const Trait for T` syntax.
534530
(active, const_trait_impl, "1.42.0", Some(67792), None),
535531

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+4-66
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
7171
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
7272
};
7373
self.check_irrefutable(&loc.pat, msg, sp);
74-
self.check_patterns(false, &loc.pat);
74+
self.check_patterns(&loc.pat);
7575
}
7676

7777
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
7878
intravisit::walk_param(self, param);
7979
self.check_irrefutable(&param.pat, "function argument", None);
80-
self.check_patterns(false, &param.pat);
80+
self.check_patterns(&param.pat);
8181
}
8282
}
8383

@@ -119,10 +119,7 @@ impl PatCtxt<'_, '_> {
119119
}
120120

121121
impl<'tcx> MatchVisitor<'_, 'tcx> {
122-
fn check_patterns(&mut self, has_guard: bool, pat: &Pat<'_>) {
123-
if !self.tcx.features().move_ref_pattern {
124-
check_legality_of_move_bindings(self, has_guard, pat);
125-
}
122+
fn check_patterns(&mut self, pat: &Pat<'_>) {
126123
pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
127124
if !self.tcx.features().bindings_after_at {
128125
check_legality_of_bindings_in_at_patterns(self, pat);
@@ -165,7 +162,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
165162
) {
166163
for arm in arms {
167164
// Check the arm for some things unrelated to exhaustiveness.
168-
self.check_patterns(arm.guard.is_some(), &arm.pat);
165+
self.check_patterns(&arm.pat);
169166
}
170167

171168
let mut cx = self.new_cx(scrut.hir_id);
@@ -601,65 +598,6 @@ fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> b
601598
!cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env)
602599
}
603600

604-
/// Check the legality of legality of by-move bindings.
605-
fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat<'_>) {
606-
let sess = cx.tcx.sess;
607-
let typeck_results = cx.typeck_results;
608-
609-
// Find all by-ref spans.
610-
let mut by_ref_spans = Vec::new();
611-
pat.each_binding(|_, hir_id, span, _| {
612-
if let Some(ty::BindByReference(_)) =
613-
typeck_results.extract_binding_mode(sess, hir_id, span)
614-
{
615-
by_ref_spans.push(span);
616-
}
617-
});
618-
619-
// Find bad by-move spans:
620-
let by_move_spans = &mut Vec::new();
621-
let mut check_move = |p: &Pat<'_>, sub: Option<&Pat<'_>>| {
622-
// Check legality of moving out of the enum.
623-
//
624-
// `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
625-
if sub.map_or(false, |p| p.contains_bindings()) {
626-
struct_span_err!(sess, p.span, E0007, "cannot bind by-move with sub-bindings")
627-
.span_label(p.span, "binds an already bound by-move value by moving it")
628-
.emit();
629-
} else if !has_guard && !by_ref_spans.is_empty() {
630-
by_move_spans.push(p.span);
631-
}
632-
};
633-
pat.walk_always(|p| {
634-
if let hir::PatKind::Binding(.., sub) = &p.kind {
635-
if let Some(ty::BindByValue(_)) =
636-
typeck_results.extract_binding_mode(sess, p.hir_id, p.span)
637-
{
638-
if is_binding_by_move(cx, p.hir_id, p.span) {
639-
check_move(p, sub.as_deref());
640-
}
641-
}
642-
}
643-
});
644-
645-
// Found some bad by-move spans, error!
646-
if !by_move_spans.is_empty() {
647-
let mut err = feature_err(
648-
&sess.parse_sess,
649-
sym::move_ref_pattern,
650-
by_move_spans.clone(),
651-
"binding by-move and by-ref in the same pattern is unstable",
652-
);
653-
for span in by_ref_spans.iter() {
654-
err.span_label(*span, "by-ref pattern here");
655-
}
656-
for span in by_move_spans.iter() {
657-
err.span_label(*span, "by-move pattern here");
658-
}
659-
err.emit();
660-
}
661-
}
662-
663601
/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
664602
///
665603
/// For example, this would reject:

src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
#![feature(or_patterns)]
44
#![feature(box_patterns)]
55

6-
#![feature(move_ref_pattern)]
7-
86
enum Test {
97
Foo,
108
Bar,

src/test/ui/borrowck/bindings-after-at-or-patterns-slice-patterns-box-patterns.stderr

+17-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: cannot borrow value as mutable because it is also borrowed as immutable
2-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:40:9
2+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:38:9
33
|
44
LL | ref foo @ [.., ref mut bar] => (),
55
| -------^^^^^^^^-----------^
@@ -8,7 +8,7 @@ LL | ref foo @ [.., ref mut bar] => (),
88
| immutable borrow, by `foo`, occurs here
99

1010
error: cannot borrow value as mutable because it is also borrowed as immutable
11-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:124:9
11+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:122:9
1212
|
1313
LL | ref foo @ Some(box ref mut s) => (),
1414
| -------^^^^^^^^^^^^---------^
@@ -17,7 +17,7 @@ LL | ref foo @ Some(box ref mut s) => (),
1717
| immutable borrow, by `foo`, occurs here
1818

1919
error[E0382]: borrow of moved value: `x`
20-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:22:5
20+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:20:5
2121
|
2222
LL | fn bindings_after_at_slice_patterns_move_binding(x: [String; 4]) {
2323
| - move occurs because `x` has type `[String; 4]`, which does not implement the `Copy` trait
@@ -29,7 +29,7 @@ LL | &x;
2929
| ^^ value borrowed here after move
3030

3131
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
32-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:32:5
32+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:30:5
3333
|
3434
LL | ref mut foo @ [.., _] => Some(foo),
3535
| --------------------- mutable borrow occurs here
@@ -41,7 +41,7 @@ LL | drop(r);
4141
| - mutable borrow later used here
4242

4343
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
44-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:54:5
44+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:52:5
4545
|
4646
LL | [ref foo @ .., ref bar] => Some(foo),
4747
| ------------ immutable borrow occurs here
@@ -53,7 +53,7 @@ LL | drop(r);
5353
| - immutable borrow later used here
5454

5555
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
56-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:66:5
56+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:64:5
5757
|
5858
LL | ref foo @ [.., ref bar] => Some(foo),
5959
| ----------------------- immutable borrow occurs here
@@ -65,7 +65,7 @@ LL | drop(r);
6565
| - immutable borrow later used here
6666

6767
error[E0382]: borrow of moved value: `x`
68-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:80:5
68+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:78:5
6969
|
7070
LL | fn bindings_after_at_or_patterns_move(x: Option<Test>) {
7171
| - move occurs because `x` has type `Option<Test>`, which does not implement the `Copy` trait
@@ -80,7 +80,7 @@ LL | &x;
8080
| ^^ value borrowed here after move
8181

8282
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
83-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:90:5
83+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:88:5
8484
|
8585
LL | ref foo @ Some(Test::Foo | Test::Bar) => Some(foo),
8686
| ------------------------------------- immutable borrow occurs here
@@ -92,7 +92,7 @@ LL | drop(r);
9292
| - immutable borrow later used here
9393

9494
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
95-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:102:5
95+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:100:5
9696
|
9797
LL | ref mut foo @ Some(Test::Foo | Test::Bar) => Some(foo),
9898
| ----------------------------------------- mutable borrow occurs here
@@ -104,7 +104,7 @@ LL | drop(r);
104104
| - mutable borrow later used here
105105

106106
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
107-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:116:5
107+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:114:5
108108
|
109109
LL | ref foo @ Some(box ref s) => Some(foo),
110110
| ------------------------- immutable borrow occurs here
@@ -116,7 +116,7 @@ LL | drop(r);
116116
| - immutable borrow later used here
117117

118118
error[E0382]: borrow of moved value: `x`
119-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:138:5
119+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:136:5
120120
|
121121
LL | fn bindings_after_at_slice_patterns_or_patterns_moves(x: [Option<Test>; 4]) {
122122
| - move occurs because `x` has type `[Option<Test>; 4]`, which does not implement the `Copy` trait
@@ -131,7 +131,7 @@ LL | &x;
131131
| ^^ value borrowed here after move
132132

133133
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
134-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:148:5
134+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:146:5
135135
|
136136
LL | ref a @ [ref b @ .., Some(Test::Foo | Test::Bar)] => Some(a),
137137
| ------------------------------------------------- immutable borrow occurs here
@@ -143,7 +143,7 @@ LL | drop(r);
143143
| - immutable borrow later used here
144144

145145
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
146-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:160:5
146+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:158:5
147147
|
148148
LL | ref a @ [ref b @ .., Some(Test::Foo | Test::Bar)] => Some(b),
149149
| ---------- immutable borrow occurs here
@@ -155,7 +155,7 @@ LL | drop(r);
155155
| - immutable borrow later used here
156156

157157
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
158-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:174:5
158+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:172:5
159159
|
160160
LL | [_, ref a @ Some(box ref b), ..] => Some(a),
161161
| ----------------------- immutable borrow occurs here
@@ -167,7 +167,7 @@ LL | drop(r);
167167
| - immutable borrow later used here
168168

169169
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
170-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:190:5
170+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:188:5
171171
|
172172
LL | [_, ref a @ Some(box Test::Foo | box Test::Bar), ..] => Some(a),
173173
| ------------------------------------------- immutable borrow occurs here
@@ -179,7 +179,7 @@ LL | drop(r);
179179
| - immutable borrow later used here
180180

181181
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
182-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:204:5
182+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:202:5
183183
|
184184
LL | [_, ref mut a @ Some(box Test::Foo | box Test::Bar), ..] => Some(a),
185185
| ----------------------------------------------- mutable borrow occurs here
@@ -191,7 +191,7 @@ LL | drop(r);
191191
| - mutable borrow later used here
192192

193193
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
194-
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:218:5
194+
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:216:5
195195
|
196196
LL | ref a @ [_, ref b @ Some(box Test::Foo | box Test::Bar), ..] => Some(a),
197197
| ------------------------------------------------------------ immutable borrow occurs here

src/test/ui/drop/dynamic-drop-async.rs

-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
// edition:2018
88
// ignore-wasm32-bare compiled with panic=abort by default
99

10-
#![feature(move_ref_pattern)]
11-
1210
#![allow(unused)]
1311

1412
use std::{

src/test/ui/drop/dynamic-drop.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// ignore-wasm32-bare compiled with panic=abort by default
33

44
#![feature(generators, generator_trait, untagged_unions)]
5-
#![feature(move_ref_pattern)]
65
#![feature(bindings_after_at)]
76

87
#![allow(unused_assignments)]

src/test/ui/error-codes/E0007.rs

-11
This file was deleted.

src/test/ui/error-codes/E0007.stderr

-22
This file was deleted.

src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.rs

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// where one side is by-ref and the other is by-move.
44

55
#![feature(bindings_after_at)]
6-
#![feature(move_ref_pattern)]
76

87
struct X {
98
x: (),

0 commit comments

Comments
 (0)