Skip to content

Commit 79fcc58

Browse files
committed
Auto merge of #54034 - pnkfelix:issue-15287-bind-by-move-pattern-guards, r=nikomatsakis
Add feature to enable bind by move pattern guards Implement #15287 as described on #15287 (comment)
2 parents f004cae + 3a07d3d commit 79fcc58

21 files changed

+318
-21
lines changed

Diff for: src/librustc/ty/context.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -1448,10 +1448,37 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
14481448
self.queries.on_disk_cache.serialize(self.global_tcx(), encoder)
14491449
}
14501450

1451+
/// This checks whether one is allowed to have pattern bindings
1452+
/// that bind-by-move on a match arm that has a guard, e.g.:
1453+
///
1454+
/// ```rust
1455+
/// match foo { A(inner) if { /* something */ } => ..., ... }
1456+
/// ```
1457+
///
1458+
/// It is separate from check_for_mutation_in_guard_via_ast_walk,
1459+
/// because that method has a narrower effect that can be toggled
1460+
/// off via a separate `-Z` flag, at least for the short term.
1461+
pub fn allow_bind_by_move_patterns_with_guards(self) -> bool {
1462+
self.features().bind_by_move_pattern_guards && self.use_mir_borrowck()
1463+
}
1464+
14511465
/// If true, we should use a naive AST walk to determine if match
14521466
/// guard could perform bad mutations (or mutable-borrows).
14531467
pub fn check_for_mutation_in_guard_via_ast_walk(self) -> bool {
1454-
!self.sess.opts.debugging_opts.disable_ast_check_for_mutation_in_guard
1468+
// If someone passes the `-Z` flag, they're asking for the footgun.
1469+
if self.sess.opts.debugging_opts.disable_ast_check_for_mutation_in_guard {
1470+
return false;
1471+
}
1472+
1473+
// If someone requests the feature, then be a little more
1474+
// careful and ensure that MIR-borrowck is enabled (which can
1475+
// happen via edition selection, via `feature(nll)`, or via an
1476+
// appropriate `-Z` flag) before disabling the mutation check.
1477+
if self.allow_bind_by_move_patterns_with_guards() {
1478+
return false;
1479+
}
1480+
1481+
return true;
14551482
}
14561483

14571484
/// If true, we should use the AST-based borrowck (we may *also* use

Diff for: src/librustc_mir/hair/pattern/check_match.rs

+19-9
Original file line numberDiff line numberDiff line change
@@ -537,11 +537,15 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
537537
"cannot bind by-move with sub-bindings")
538538
.span_label(p.span, "binds an already bound by-move value by moving it")
539539
.emit();
540-
} else if has_guard {
541-
struct_span_err!(cx.tcx.sess, p.span, E0008,
542-
"cannot bind by-move into a pattern guard")
543-
.span_label(p.span, "moves value into pattern guard")
544-
.emit();
540+
} else if has_guard && !cx.tcx.allow_bind_by_move_patterns_with_guards() {
541+
let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008,
542+
"cannot bind by-move into a pattern guard");
543+
err.span_label(p.span, "moves value into pattern guard");
544+
if cx.tcx.sess.opts.unstable_features.is_nightly_build() && cx.tcx.use_mir_borrowck() {
545+
err.help("add #![feature(bind_by_move_pattern_guards)] to the \
546+
crate attributes to enable");
547+
}
548+
err.emit();
545549
} else if let Some(by_ref_span) = by_ref_span {
546550
struct_span_err!(
547551
cx.tcx.sess,
@@ -613,10 +617,16 @@ impl<'a, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'tcx> {
613617
_: LoanCause) {
614618
match kind {
615619
ty::MutBorrow => {
616-
struct_span_err!(self.cx.tcx.sess, span, E0301,
617-
"cannot mutably borrow in a pattern guard")
618-
.span_label(span, "borrowed mutably in pattern guard")
619-
.emit();
620+
let mut err = struct_span_err!(self.cx.tcx.sess, span, E0301,
621+
"cannot mutably borrow in a pattern guard");
622+
err.span_label(span, "borrowed mutably in pattern guard");
623+
if self.cx.tcx.sess.opts.unstable_features.is_nightly_build() &&
624+
self.cx.tcx.use_mir_borrowck()
625+
{
626+
err.help("add #![feature(bind_by_move_pattern_guards)] to the \
627+
crate attributes to enable");
628+
}
629+
err.emit();
620630
}
621631
ty::ImmBorrow | ty::UniqueImmBorrow => {}
622632
}

Diff for: src/libsyntax/feature_gate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,12 @@ declare_features! (
515515

516516
// Self struct constructor (RFC 2302)
517517
(active, self_struct_ctor, "1.31.0", Some(51994), None),
518+
519+
// allow mixing of bind-by-move in patterns and references to
520+
// those identifiers in guards, *if* we are using MIR-borrowck
521+
// (aka NLL). Essentially this means you need to be on
522+
// edition:2018 or later.
523+
(active, bind_by_move_pattern_guards, "1.30.0", Some(15287), None),
518524
);
519525

520526
declare_features! (
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0008]: cannot bind by-move into a pattern guard
2+
--> $DIR/bind-by-move-no-guards.rs:8:14
3+
|
4+
LL | Some(z) if z.recv().unwrap() => { panic!() },
5+
| ^ moves value into pattern guard
6+
|
7+
= help: add #![feature(bind_by_move_pattern_guards)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0008`.

Diff for: src/test/ui/bind-by-move/bind-by-move-no-guards.rs

-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2-
// file at the top-level directory of this distribution and at
3-
// http://rust-lang.org/COPYRIGHT.
4-
//
5-
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6-
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7-
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8-
// option. This file may not be copied, modified, or distributed
9-
// except according to those terms.
10-
111
use std::sync::mpsc::channel;
122

133
fn main() {

Diff for: src/test/ui/bind-by-move/bind-by-move-no-guards.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0008]: cannot bind by-move into a pattern guard
2-
--> $DIR/bind-by-move-no-guards.rs:18:14
2+
--> $DIR/bind-by-move-no-guards.rs:8:14
33
|
44
LL | Some(z) if z.recv().unwrap() => { panic!() },
55
| ^ moves value into pattern guard
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0302]: cannot assign in a pattern guard
2+
--> $DIR/borrowck-mutate-in-guard.rs:20:25
3+
|
4+
LL | Enum::A(_) if { x = Enum::B(false); false } => 1,
5+
| ^^^^^^^^^^^^^^^^^^ assignment in pattern guard
6+
7+
error[E0301]: cannot mutably borrow in a pattern guard
8+
--> $DIR/borrowck-mutate-in-guard.rs:22:38
9+
|
10+
LL | Enum::A(_) if { let y = &mut x; *y = Enum::B(false); false } => 1,
11+
| ^ borrowed mutably in pattern guard
12+
|
13+
= help: add #![feature(bind_by_move_pattern_guards)] to the crate attributes to enable
14+
15+
error[E0302]: cannot assign in a pattern guard
16+
--> $DIR/borrowck-mutate-in-guard.rs:22:41
17+
|
18+
LL | Enum::A(_) if { let y = &mut x; *y = Enum::B(false); false } => 1,
19+
| ^^^^^^^^^^^^^^^^^^^ assignment in pattern guard
20+
21+
error: aborting due to 3 previous errors
22+
23+
Some errors occurred: E0301, E0302.
24+
For more information about an error, try `rustc --explain E0301`.

Diff for: src/test/ui/error-codes/E0008.nll.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0008]: cannot bind by-move into a pattern guard
2+
--> $DIR/E0008.rs:13:14
3+
|
4+
LL | Some(s) if s.len() == 0 => {},
5+
| ^ moves value into pattern guard
6+
|
7+
= help: add #![feature(bind_by_move_pattern_guards)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0008`.

Diff for: src/test/ui/error-codes/E0301.nll.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0301]: cannot mutably borrow in a pattern guard
2+
--> $DIR/E0301.rs:14:19
3+
|
4+
LL | option if option.take().is_none() => {}, //~ ERROR E0301
5+
| ^^^^^^ borrowed mutably in pattern guard
6+
|
7+
= help: add #![feature(bind_by_move_pattern_guards)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0301`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Adaptation of existing ui test (from way back in
2+
// rust-lang/rust#2329), that starts passing with this feature in
3+
// place.
4+
5+
// compile-pass
6+
7+
#![feature(nll)]
8+
#![feature(bind_by_move_pattern_guards)]
9+
10+
use std::sync::mpsc::channel;
11+
12+
fn main() {
13+
let (tx, rx) = channel();
14+
let x = Some(rx);
15+
tx.send(false);
16+
match x {
17+
Some(z) if z.recv().unwrap() => { panic!() },
18+
Some(z) => { assert!(!z.recv().unwrap()); },
19+
None => panic!()
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0008]: cannot bind by-move into a pattern guard
2+
--> $DIR/feature-gate.rs:33:16
3+
|
4+
LL | A { a: v } if *v == 42 => v,
5+
| ^ moves value into pattern guard
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0008`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: compilation successful
2+
--> $DIR/feature-gate.rs:42:1
3+
|
4+
LL | / fn main() {
5+
LL | | foo(107)
6+
LL | | }
7+
| |_^
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: compilation successful
2+
--> $DIR/feature-gate.rs:42:1
3+
|
4+
LL | / fn main() {
5+
LL | | foo(107)
6+
LL | | }
7+
| |_^
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: compilation successful
2+
--> $DIR/feature-gate.rs:42:1
3+
|
4+
LL | / fn main() {
5+
LL | | foo(107)
6+
LL | | }
7+
| |_^
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0008]: cannot bind by-move into a pattern guard
2+
--> $DIR/feature-gate.rs:33:16
3+
|
4+
LL | A { a: v } if *v == 42 => v,
5+
| ^ moves value into pattern guard
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0008`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Check that pattern-guards with move-bound variables is only allowed
2+
// with the appropriate set of feature gates. (Note that we require
3+
// the code to opt into MIR-borrowck in *some* way before the feature
4+
// will work; we use the revision system here to enumerate a number of
5+
// ways that opt-in could occur.)
6+
7+
// gate-test-bind_by_move_pattern_guards
8+
9+
// revisions: no_gate gate_and_2015 gate_and_2018 gate_and_znll gate_and_feature_nll
10+
11+
// (We're already testing NLL behavior quite explicitly, no need for compare-mode=nll.)
12+
// ignore-compare-mode-nll
13+
14+
#![feature(rustc_attrs)]
15+
16+
#![cfg_attr(gate_and_2015, feature(bind_by_move_pattern_guards))]
17+
#![cfg_attr(gate_and_2018, feature(bind_by_move_pattern_guards))]
18+
#![cfg_attr(gate_and_znll, feature(bind_by_move_pattern_guards))]
19+
#![cfg_attr(gate_and_feature_nll, feature(bind_by_move_pattern_guards))]
20+
21+
#![cfg_attr(gate_and_feature_nll, feature(nll))]
22+
23+
//[gate_and_2015] edition:2015
24+
//[gate_and_2018] edition:2018
25+
//[gate_and_znll] compile-flags: -Z borrowck=mir
26+
27+
struct A { a: Box<i32> }
28+
29+
fn foo(n: i32) {
30+
let x = A { a: Box::new(n) };
31+
let _y = match x {
32+
33+
A { a: v } if *v == 42 => v,
34+
//[no_gate]~^ ERROR cannot bind by-move into a pattern guard
35+
//[gate_and_2015]~^^ ERROR cannot bind by-move into a pattern guard
36+
37+
_ => Box::new(0)
38+
};
39+
}
40+
41+
#[rustc_error]
42+
fn main() {
43+
foo(107)
44+
}
45+
//[gate_and_2018]~^^^ ERROR compilation successful
46+
//[gate_and_znll]~^^^^ ERROR compilation successful
47+
//[gate_and_feature_nll]~^^^^^ ERROR compilation successful
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#![feature(nll)]
2+
#![feature(bind_by_move_pattern_guards)]
3+
4+
// compile-pass
5+
6+
struct A { a: Box<i32> }
7+
8+
impl A {
9+
fn get(&self) -> i32 { *self.a }
10+
}
11+
12+
fn foo(n: i32) {
13+
let x = A { a: Box::new(n) };
14+
let y = match x {
15+
A { a: v } if *v == 42 => v,
16+
_ => Box::new(0),
17+
};
18+
}
19+
20+
fn bar(n: i32) {
21+
let x = A { a: Box::new(n) };
22+
let y = match x {
23+
A { a: v } if x.get() == 42 => v,
24+
_ => Box::new(0),
25+
};
26+
}
27+
28+
fn baz(n: i32) {
29+
let x = A { a: Box::new(n) };
30+
let y = match x {
31+
A { a: v } if *v.clone() == 42 => v,
32+
_ => Box::new(0),
33+
};
34+
}
35+
36+
fn main() {
37+
foo(107);
38+
bar(107);
39+
baz(107);
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![feature(nll)]
2+
#![feature(bind_by_move_pattern_guards)]
3+
4+
enum VecWrapper { A(Vec<i32>) }
5+
6+
fn foo(x: VecWrapper) -> usize {
7+
match x {
8+
VecWrapper::A(v) if { drop(v); false } => 1,
9+
//~^ ERROR cannot move out of borrowed content
10+
VecWrapper::A(v) => v.len()
11+
}
12+
}
13+
14+
fn main() {
15+
foo(VecWrapper::A(vec![107]));
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0507]: cannot move out of borrowed content
2+
--> $DIR/rfc-reject-double-move-across-arms.rs:8:36
3+
|
4+
LL | VecWrapper::A(v) if { drop(v); false } => 1,
5+
| ^ cannot move out of borrowed content
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0507`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(nll)]
2+
#![feature(bind_by_move_pattern_guards)]
3+
4+
struct A { a: Box<i32> }
5+
6+
fn foo(n: i32) {
7+
let x = A { a: Box::new(n) };
8+
let _y = match x {
9+
A { a: v } if { drop(v); true } => v,
10+
//~^ ERROR cannot move out of borrowed content
11+
_ => Box::new(0),
12+
};
13+
}
14+
15+
fn main() {
16+
foo(107);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0507]: cannot move out of borrowed content
2+
--> $DIR/rfc-reject-double-move-in-first-arm.rs:9:30
3+
|
4+
LL | A { a: v } if { drop(v); true } => v,
5+
| ^ cannot move out of borrowed content
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0507`.

0 commit comments

Comments
 (0)