diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 6738267b5b8c..43bd82118c66 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1436,10 +1436,37 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.queries.on_disk_cache.serialize(self.global_tcx(), encoder) } + /// This checks whether one is allowed to have pattern bindings + /// that bind-by-move on a match arm that has a guard, e.g.: + /// + /// ```rust + /// match foo { A(inner) if { /* something */ } => ..., ... } + /// ``` + /// + /// It is separate from check_for_mutation_in_guard_via_ast_walk, + /// because that method has a narrower effect that can be toggled + /// off via a separate `-Z` flag, at least for the short term. + pub fn allow_bind_by_move_patterns_with_guards(self) -> bool { + self.features().bind_by_move_pattern_guards && self.use_mir_borrowck() + } + /// If true, we should use a naive AST walk to determine if match /// guard could perform bad mutations (or mutable-borrows). pub fn check_for_mutation_in_guard_via_ast_walk(self) -> bool { - !self.sess.opts.debugging_opts.disable_ast_check_for_mutation_in_guard + // If someone passes the `-Z` flag, they're asking for the footgun. + if self.sess.opts.debugging_opts.disable_ast_check_for_mutation_in_guard { + return false; + } + + // If someone requests the feature, then be a little more + // careful and ensure that MIR-borrowck is enabled (which can + // happen via edition selection, via `feature(nll)`, or via an + // appropriate `-Z` flag) before disabling the mutation check. + if self.allow_bind_by_move_patterns_with_guards() { + return false; + } + + return true; } /// If true, we should use the AST-based borrowck (we may *also* use diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index bf878145e1fb..23667d1b331a 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -537,11 +537,15 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor, "cannot bind by-move with sub-bindings") .span_label(p.span, "binds an already bound by-move value by moving it") .emit(); - } else if has_guard { - struct_span_err!(cx.tcx.sess, p.span, E0008, - "cannot bind by-move into a pattern guard") - .span_label(p.span, "moves value into pattern guard") - .emit(); + } else if has_guard && !cx.tcx.allow_bind_by_move_patterns_with_guards() { + let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008, + "cannot bind by-move into a pattern guard"); + err.span_label(p.span, "moves value into pattern guard"); + if cx.tcx.sess.opts.unstable_features.is_nightly_build() && cx.tcx.use_mir_borrowck() { + err.help("add #![feature(bind_by_move_pattern_guards)] to the \ + crate attributes to enable"); + } + err.emit(); } else if let Some(by_ref_span) = by_ref_span { struct_span_err!( cx.tcx.sess, @@ -613,10 +617,16 @@ impl<'a, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'tcx> { _: LoanCause) { match kind { ty::MutBorrow => { - struct_span_err!(self.cx.tcx.sess, span, E0301, - "cannot mutably borrow in a pattern guard") - .span_label(span, "borrowed mutably in pattern guard") - .emit(); + let mut err = struct_span_err!(self.cx.tcx.sess, span, E0301, + "cannot mutably borrow in a pattern guard"); + err.span_label(span, "borrowed mutably in pattern guard"); + if self.cx.tcx.sess.opts.unstable_features.is_nightly_build() && + self.cx.tcx.use_mir_borrowck() + { + err.help("add #![feature(bind_by_move_pattern_guards)] to the \ + crate attributes to enable"); + } + err.emit(); } ty::ImmBorrow | ty::UniqueImmBorrow => {} } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 7266d807d3ba..922e773f1443 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -515,6 +515,12 @@ declare_features! ( // Self struct constructor (RFC 2302) (active, self_struct_ctor, "1.31.0", Some(51994), None), + + // allow mixing of bind-by-move in patterns and references to + // those identifiers in guards, *if* we are using MIR-borrowck + // (aka NLL). Essentially this means you need to be on + // edition:2018 or later. + (active, bind_by_move_pattern_guards, "1.30.0", Some(15287), None), ); declare_features! ( diff --git a/src/test/ui/bind-by-move/bind-by-move-no-guards.nll.stderr b/src/test/ui/bind-by-move/bind-by-move-no-guards.nll.stderr new file mode 100644 index 000000000000..5f8b7007f304 --- /dev/null +++ b/src/test/ui/bind-by-move/bind-by-move-no-guards.nll.stderr @@ -0,0 +1,11 @@ +error[E0008]: cannot bind by-move into a pattern guard + --> $DIR/bind-by-move-no-guards.rs:8:14 + | +LL | Some(z) if z.recv().unwrap() => { panic!() }, + | ^ moves value into pattern guard + | + = help: add #![feature(bind_by_move_pattern_guards)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0008`. diff --git a/src/test/ui/bind-by-move/bind-by-move-no-guards.rs b/src/test/ui/bind-by-move/bind-by-move-no-guards.rs index bb6060f2543e..bc9b3a8de4ef 100644 --- a/src/test/ui/bind-by-move/bind-by-move-no-guards.rs +++ b/src/test/ui/bind-by-move/bind-by-move-no-guards.rs @@ -1,13 +1,3 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - use std::sync::mpsc::channel; fn main() { diff --git a/src/test/ui/bind-by-move/bind-by-move-no-guards.stderr b/src/test/ui/bind-by-move/bind-by-move-no-guards.stderr index ed516cd559eb..2af2b0d660ef 100644 --- a/src/test/ui/bind-by-move/bind-by-move-no-guards.stderr +++ b/src/test/ui/bind-by-move/bind-by-move-no-guards.stderr @@ -1,5 +1,5 @@ error[E0008]: cannot bind by-move into a pattern guard - --> $DIR/bind-by-move-no-guards.rs:18:14 + --> $DIR/bind-by-move-no-guards.rs:8:14 | LL | Some(z) if z.recv().unwrap() => { panic!() }, | ^ moves value into pattern guard diff --git a/src/test/ui/borrowck/borrowck-mutate-in-guard.nll.stderr b/src/test/ui/borrowck/borrowck-mutate-in-guard.nll.stderr new file mode 100644 index 000000000000..b363a78cbc20 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-mutate-in-guard.nll.stderr @@ -0,0 +1,24 @@ +error[E0302]: cannot assign in a pattern guard + --> $DIR/borrowck-mutate-in-guard.rs:20:25 + | +LL | Enum::A(_) if { x = Enum::B(false); false } => 1, + | ^^^^^^^^^^^^^^^^^^ assignment in pattern guard + +error[E0301]: cannot mutably borrow in a pattern guard + --> $DIR/borrowck-mutate-in-guard.rs:22:38 + | +LL | Enum::A(_) if { let y = &mut x; *y = Enum::B(false); false } => 1, + | ^ borrowed mutably in pattern guard + | + = help: add #![feature(bind_by_move_pattern_guards)] to the crate attributes to enable + +error[E0302]: cannot assign in a pattern guard + --> $DIR/borrowck-mutate-in-guard.rs:22:41 + | +LL | Enum::A(_) if { let y = &mut x; *y = Enum::B(false); false } => 1, + | ^^^^^^^^^^^^^^^^^^^ assignment in pattern guard + +error: aborting due to 3 previous errors + +Some errors occurred: E0301, E0302. +For more information about an error, try `rustc --explain E0301`. diff --git a/src/test/ui/error-codes/E0008.nll.stderr b/src/test/ui/error-codes/E0008.nll.stderr new file mode 100644 index 000000000000..ce627cb741a3 --- /dev/null +++ b/src/test/ui/error-codes/E0008.nll.stderr @@ -0,0 +1,11 @@ +error[E0008]: cannot bind by-move into a pattern guard + --> $DIR/E0008.rs:13:14 + | +LL | Some(s) if s.len() == 0 => {}, + | ^ moves value into pattern guard + | + = help: add #![feature(bind_by_move_pattern_guards)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0008`. diff --git a/src/test/ui/error-codes/E0301.nll.stderr b/src/test/ui/error-codes/E0301.nll.stderr new file mode 100644 index 000000000000..f060eb904353 --- /dev/null +++ b/src/test/ui/error-codes/E0301.nll.stderr @@ -0,0 +1,11 @@ +error[E0301]: cannot mutably borrow in a pattern guard + --> $DIR/E0301.rs:14:19 + | +LL | option if option.take().is_none() => {}, //~ ERROR E0301 + | ^^^^^^ borrowed mutably in pattern guard + | + = help: add #![feature(bind_by_move_pattern_guards)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0301`. diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs new file mode 100644 index 000000000000..2f3c094ff395 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs @@ -0,0 +1,21 @@ +// Adaptation of existing ui test (from way back in +// rust-lang/rust#2329), that starts passing with this feature in +// place. + +// compile-pass + +#![feature(nll)] +#![feature(bind_by_move_pattern_guards)] + +use std::sync::mpsc::channel; + +fn main() { + let (tx, rx) = channel(); + let x = Some(rx); + tx.send(false); + match x { + Some(z) if z.recv().unwrap() => { panic!() }, + Some(z) => { assert!(!z.recv().unwrap()); }, + None => panic!() + } +} diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_2015.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_2015.stderr new file mode 100644 index 000000000000..4c17ce23b376 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_2015.stderr @@ -0,0 +1,9 @@ +error[E0008]: cannot bind by-move into a pattern guard + --> $DIR/feature-gate.rs:33:16 + | +LL | A { a: v } if *v == 42 => v, + | ^ moves value into pattern guard + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0008`. diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_2018.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_2018.stderr new file mode 100644 index 000000000000..4bde9b0c8d91 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_2018.stderr @@ -0,0 +1,10 @@ +error: compilation successful + --> $DIR/feature-gate.rs:42:1 + | +LL | / fn main() { +LL | | foo(107) +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_feature_nll.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_feature_nll.stderr new file mode 100644 index 000000000000..4bde9b0c8d91 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_feature_nll.stderr @@ -0,0 +1,10 @@ +error: compilation successful + --> $DIR/feature-gate.rs:42:1 + | +LL | / fn main() { +LL | | foo(107) +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_znll.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_znll.stderr new file mode 100644 index 000000000000..4bde9b0c8d91 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.gate_and_znll.stderr @@ -0,0 +1,10 @@ +error: compilation successful + --> $DIR/feature-gate.rs:42:1 + | +LL | / fn main() { +LL | | foo(107) +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.no_gate.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.no_gate.stderr new file mode 100644 index 000000000000..4c17ce23b376 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.no_gate.stderr @@ -0,0 +1,9 @@ +error[E0008]: cannot bind by-move into a pattern guard + --> $DIR/feature-gate.rs:33:16 + | +LL | A { a: v } if *v == 42 => v, + | ^ moves value into pattern guard + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0008`. diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.rs b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.rs new file mode 100644 index 000000000000..f6df4d07baad --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/feature-gate.rs @@ -0,0 +1,47 @@ +// Check that pattern-guards with move-bound variables is only allowed +// with the appropriate set of feature gates. (Note that we require +// the code to opt into MIR-borrowck in *some* way before the feature +// will work; we use the revision system here to enumerate a number of +// ways that opt-in could occur.) + +// gate-test-bind_by_move_pattern_guards + +// revisions: no_gate gate_and_2015 gate_and_2018 gate_and_znll gate_and_feature_nll + +// (We're already testing NLL behavior quite explicitly, no need for compare-mode=nll.) +// ignore-compare-mode-nll + +#![feature(rustc_attrs)] + +#![cfg_attr(gate_and_2015, feature(bind_by_move_pattern_guards))] +#![cfg_attr(gate_and_2018, feature(bind_by_move_pattern_guards))] +#![cfg_attr(gate_and_znll, feature(bind_by_move_pattern_guards))] +#![cfg_attr(gate_and_feature_nll, feature(bind_by_move_pattern_guards))] + +#![cfg_attr(gate_and_feature_nll, feature(nll))] + +//[gate_and_2015] edition:2015 +//[gate_and_2018] edition:2018 +//[gate_and_znll] compile-flags: -Z borrowck=mir + +struct A { a: Box } + +fn foo(n: i32) { + let x = A { a: Box::new(n) }; + let _y = match x { + + A { a: v } if *v == 42 => v, + //[no_gate]~^ ERROR cannot bind by-move into a pattern guard + //[gate_and_2015]~^^ ERROR cannot bind by-move into a pattern guard + + _ => Box::new(0) + }; +} + +#[rustc_error] +fn main() { + foo(107) +} +//[gate_and_2018]~^^^ ERROR compilation successful +//[gate_and_znll]~^^^^ ERROR compilation successful +//[gate_and_feature_nll]~^^^^^ ERROR compilation successful diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs new file mode 100644 index 000000000000..9a9d11ce1b13 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs @@ -0,0 +1,40 @@ +#![feature(nll)] +#![feature(bind_by_move_pattern_guards)] + +// compile-pass + +struct A { a: Box } + +impl A { + fn get(&self) -> i32 { *self.a } +} + +fn foo(n: i32) { + let x = A { a: Box::new(n) }; + let y = match x { + A { a: v } if *v == 42 => v, + _ => Box::new(0), + }; +} + +fn bar(n: i32) { + let x = A { a: Box::new(n) }; + let y = match x { + A { a: v } if x.get() == 42 => v, + _ => Box::new(0), + }; +} + +fn baz(n: i32) { + let x = A { a: Box::new(n) }; + let y = match x { + A { a: v } if *v.clone() == 42 => v, + _ => Box::new(0), + }; +} + +fn main() { + foo(107); + bar(107); + baz(107); +} diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs new file mode 100644 index 000000000000..0fec6b273d3b --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs @@ -0,0 +1,16 @@ +#![feature(nll)] +#![feature(bind_by_move_pattern_guards)] + +enum VecWrapper { A(Vec) } + +fn foo(x: VecWrapper) -> usize { + match x { + VecWrapper::A(v) if { drop(v); false } => 1, + //~^ ERROR cannot move out of borrowed content + VecWrapper::A(v) => v.len() + } +} + +fn main() { + foo(VecWrapper::A(vec![107])); +} diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr new file mode 100644 index 000000000000..502006e1b3f9 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr @@ -0,0 +1,9 @@ +error[E0507]: cannot move out of borrowed content + --> $DIR/rfc-reject-double-move-across-arms.rs:8:36 + | +LL | VecWrapper::A(v) if { drop(v); false } => 1, + | ^ cannot move out of borrowed content + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs new file mode 100644 index 000000000000..396bfc1c9319 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs @@ -0,0 +1,17 @@ +#![feature(nll)] +#![feature(bind_by_move_pattern_guards)] + +struct A { a: Box } + +fn foo(n: i32) { + let x = A { a: Box::new(n) }; + let _y = match x { + A { a: v } if { drop(v); true } => v, + //~^ ERROR cannot move out of borrowed content + _ => Box::new(0), + }; +} + +fn main() { + foo(107); +} diff --git a/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr new file mode 100644 index 000000000000..dd8f42f74970 --- /dev/null +++ b/src/test/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr @@ -0,0 +1,9 @@ +error[E0507]: cannot move out of borrowed content + --> $DIR/rfc-reject-double-move-in-first-arm.rs:9:30 + | +LL | A { a: v } if { drop(v); true } => v, + | ^ cannot move out of borrowed content + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`.