Skip to content

Commit 11a913c

Browse files
authored
Rollup merge of rust-lang#63059 - Centril:sound-bind-by-move, r=matthewjasper
Make `#![feature(bind_by_move_pattern_guards)]` sound without `#[feature(nll)]` Implements rust-lang#15287 (comment). Fixes rust-lang#31287 Fixes rust-lang#27282 r? @matthewjasper
2 parents 4838953 + cd79609 commit 11a913c

34 files changed

+204
-185
lines changed

src/librustc/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ rustc_queries! {
461461
}
462462

463463
TypeChecking {
464-
query check_match(key: DefId) -> () {
464+
query check_match(key: DefId) -> SignalledError {
465465
cache_on_disk_if { key.is_local() }
466466
}
467467

src/librustc/ty/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::hir::def::{DefKind, Export};
44
use crate::hir::{self, TraitCandidate, ItemLocalId, CodegenFnAttrs};
55
use crate::infer::canonical::{self, Canonical};
66
use crate::lint;
7-
use crate::middle::borrowck::BorrowCheckResult;
7+
use crate::middle::borrowck::{BorrowCheckResult, SignalledError};
88
use crate::middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary, ForeignModule};
99
use crate::middle::cstore::{NativeLibraryKind, DepKind, CrateSource};
1010
use crate::middle::privacy::AccessLevels;

src/librustc_ast_borrowck/borrowck/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ fn borrowck(tcx: TyCtxt<'_>, owner_def_id: DefId) -> &BorrowCheckResult {
6666

6767
debug!("borrowck(body_owner_def_id={:?})", owner_def_id);
6868

69+
let signalled_error = tcx.check_match(owner_def_id);
70+
if let SignalledError::SawSomeError = signalled_error {
71+
return tcx.arena.alloc(BorrowCheckResult {
72+
signalled_any_error: SignalledError::SawSomeError,
73+
})
74+
}
75+
6976
let owner_id = tcx.hir().as_local_hir_id(owner_def_id).unwrap();
7077

7178
match tcx.hir().get(owner_id) {

src/librustc_mir/error_codes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1989,7 +1989,7 @@ When matching on a variable it cannot be mutated in the match guards, as this
19891989
could cause the match to be non-exhaustive:
19901990
19911991
```compile_fail,E0510
1992-
#![feature(nll, bind_by_move_pattern_guards)]
1992+
#![feature(bind_by_move_pattern_guards)]
19931993
let mut x = Some(0);
19941994
match x {
19951995
None => (),

src/librustc_mir/hair/pattern/check_match.rs

+34-24
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::_match::WitnessPreference::*;
44

55
use super::{Pattern, PatternContext, PatternError, PatternKind};
66

7+
use rustc::middle::borrowck::SignalledError;
78
use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor};
89
use rustc::middle::expr_use_visitor::{LoanCause, MutateMode};
910
use rustc::middle::expr_use_visitor as euv;
@@ -26,21 +27,24 @@ use std::slice;
2627

2728
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
2829

29-
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
30+
crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) -> SignalledError {
3031
let body_id = if let Some(id) = tcx.hir().as_local_hir_id(def_id) {
3132
tcx.hir().body_owned_by(id)
3233
} else {
33-
return;
34+
return SignalledError::NoErrorsSeen;
3435
};
3536

36-
MatchVisitor {
37+
let mut visitor = MatchVisitor {
3738
tcx,
3839
body_owner: def_id,
3940
tables: tcx.body_tables(body_id),
4041
region_scope_tree: &tcx.region_scope_tree(def_id),
4142
param_env: tcx.param_env(def_id),
4243
identity_substs: InternalSubsts::identity_for_item(tcx, def_id),
43-
}.visit_body(tcx.hir().body(body_id));
44+
signalled_error: SignalledError::NoErrorsSeen,
45+
};
46+
visitor.visit_body(tcx.hir().body(body_id));
47+
visitor.signalled_error
4448
}
4549

4650
fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
@@ -54,6 +58,7 @@ struct MatchVisitor<'a, 'tcx> {
5458
param_env: ty::ParamEnv<'tcx>,
5559
identity_substs: SubstsRef<'tcx>,
5660
region_scope_tree: &'a region::ScopeTree,
61+
signalled_error: SignalledError,
5762
}
5863

5964
impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
@@ -64,11 +69,8 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
6469
fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
6570
intravisit::walk_expr(self, ex);
6671

67-
match ex.node {
68-
hir::ExprKind::Match(ref scrut, ref arms, source) => {
69-
self.check_match(scrut, arms, source);
70-
}
71-
_ => {}
72+
if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.node {
73+
self.check_match(scrut, arms, source);
7274
}
7375
}
7476

@@ -130,26 +132,27 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
130132
}
131133

132134
impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
133-
fn check_patterns(&self, has_guard: bool, pats: &[P<Pat>]) {
135+
fn check_patterns(&mut self, has_guard: bool, pats: &[P<Pat>]) {
134136
check_legality_of_move_bindings(self, has_guard, pats);
135137
for pat in pats {
136138
check_legality_of_bindings_in_at_patterns(self, pat);
137139
}
138140
}
139141

140142
fn check_match(
141-
&self,
143+
&mut self,
142144
scrut: &hir::Expr,
143145
arms: &'tcx [hir::Arm],
144-
source: hir::MatchSource)
145-
{
146+
source: hir::MatchSource
147+
) {
146148
for arm in arms {
147149
// First, check legality of move bindings.
148150
self.check_patterns(arm.guard.is_some(), &arm.pats);
149151

150152
// Second, if there is a guard on each arm, make sure it isn't
151153
// assigning or borrowing anything mutably.
152154
if let Some(ref guard) = arm.guard {
155+
self.signalled_error = SignalledError::SawSomeError;
153156
if !self.tcx.features().bind_by_move_pattern_guards {
154157
check_for_mutation_in_guard(self, &guard);
155158
}
@@ -548,7 +551,7 @@ fn maybe_point_at_variant(
548551

549552
// Legality of move bindings checking
550553
fn check_legality_of_move_bindings(
551-
cx: &MatchVisitor<'_, '_>,
554+
cx: &mut MatchVisitor<'_, '_>,
552555
has_guard: bool,
553556
pats: &[P<Pat>],
554557
) {
@@ -565,7 +568,12 @@ fn check_legality_of_move_bindings(
565568
})
566569
}
567570
let span_vec = &mut Vec::new();
568-
let check_move = |p: &Pat, sub: Option<&Pat>, span_vec: &mut Vec<Span>| {
571+
let check_move = |
572+
cx: &mut MatchVisitor<'_, '_>,
573+
p: &Pat,
574+
sub: Option<&Pat>,
575+
span_vec: &mut Vec<Span>,
576+
| {
569577
// check legality of moving out of the enum
570578

571579
// x @ Foo(..) is legal, but x @ Foo(y) isn't.
@@ -574,15 +582,17 @@ fn check_legality_of_move_bindings(
574582
"cannot bind by-move with sub-bindings")
575583
.span_label(p.span, "binds an already bound by-move value by moving it")
576584
.emit();
577-
} else if has_guard && !cx.tcx.features().bind_by_move_pattern_guards {
578-
let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008,
579-
"cannot bind by-move into a pattern guard");
580-
err.span_label(p.span, "moves value into pattern guard");
581-
if cx.tcx.sess.opts.unstable_features.is_nightly_build() {
582-
err.help("add `#![feature(bind_by_move_pattern_guards)]` to the \
583-
crate attributes to enable");
585+
} else if has_guard {
586+
if !cx.tcx.features().bind_by_move_pattern_guards {
587+
let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008,
588+
"cannot bind by-move into a pattern guard");
589+
err.span_label(p.span, "moves value into pattern guard");
590+
if cx.tcx.sess.opts.unstable_features.is_nightly_build() {
591+
err.help("add `#![feature(bind_by_move_pattern_guards)]` to the \
592+
crate attributes to enable");
593+
}
594+
err.emit();
584595
}
585-
err.emit();
586596
} else if let Some(_by_ref_span) = by_ref_span {
587597
span_vec.push(p.span);
588598
}
@@ -596,7 +606,7 @@ fn check_legality_of_move_bindings(
596606
ty::BindByValue(..) => {
597607
let pat_ty = cx.tables.node_type(p.hir_id);
598608
if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) {
599-
check_move(p, sub.as_ref().map(|p| &**p), span_vec);
609+
check_move(cx, p, sub.as_ref().map(|p| &**p), span_vec);
600610
}
601611
}
602612
_ => {}

src/test/ui/borrowck/borrowck-migrate-to-nll.edition.stderr

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
warning[E0507]: cannot move out of `foo` in pattern guard
2-
--> $DIR/borrowck-migrate-to-nll.rs:26:18
1+
warning[E0502]: cannot borrow `*block.current` as immutable because it is also borrowed as mutable
2+
--> $DIR/borrowck-migrate-to-nll.rs:28:21
33
|
4-
LL | (|| { let bar = foo; bar.take() })();
5-
| ^^ ---
6-
| | |
7-
| | move occurs because `foo` has type `&mut std::option::Option<&i32>`, which does not implement the `Copy` trait
8-
| | move occurs due to use in closure
9-
| move out of `foo` occurs here
4+
LL | let x = &mut block;
5+
| ---------- mutable borrow occurs here
6+
LL | let p: &'a u8 = &*block.current;
7+
| ^^^^^^^^^^^^^^^ immutable borrow occurs here
8+
LL | // (use `x` and `p` so enabling NLL doesn't assign overly short lifetimes)
9+
LL | drop(x);
10+
| - mutable borrow later used here
1011
|
11-
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
1212
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
1313
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
1414
= note: for more information, try `rustc --explain E0729`
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// This is a test of the borrowck migrate mode. It leverages #27282, a
1+
// This is a test of the borrowck migrate mode. It leverages #38899, a
22
// bug that is fixed by NLL: this code is (unsoundly) accepted by
33
// AST-borrowck, but is correctly rejected by the NLL borrowck.
44
//
@@ -18,15 +18,17 @@
1818
//[zflag] run-pass
1919
//[edition] run-pass
2020

21-
fn main() {
22-
match Some(&4) {
23-
None => {},
24-
ref mut foo
25-
if {
26-
(|| { let bar = foo; bar.take() })();
27-
false
28-
} => {},
29-
Some(ref _s) => println!("Note this arm is bogus; the `Some` became `None` in the guard."),
30-
_ => println!("Here is some supposedly unreachable code."),
31-
}
21+
pub struct Block<'a> {
22+
current: &'a u8,
23+
unrelated: &'a u8,
3224
}
25+
26+
fn bump<'a>(mut block: &mut Block<'a>) {
27+
let x = &mut block;
28+
let p: &'a u8 = &*block.current;
29+
// (use `x` and `p` so enabling NLL doesn't assign overly short lifetimes)
30+
drop(x);
31+
drop(p);
32+
}
33+
34+
fn main() {}

src/test/ui/borrowck/borrowck-migrate-to-nll.zflag.stderr

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
warning[E0507]: cannot move out of `foo` in pattern guard
2-
--> $DIR/borrowck-migrate-to-nll.rs:26:18
1+
warning[E0502]: cannot borrow `*block.current` as immutable because it is also borrowed as mutable
2+
--> $DIR/borrowck-migrate-to-nll.rs:28:21
33
|
4-
LL | (|| { let bar = foo; bar.take() })();
5-
| ^^ ---
6-
| | |
7-
| | move occurs because `foo` has type `&mut std::option::Option<&i32>`, which does not implement the `Copy` trait
8-
| | move occurs due to use in closure
9-
| move out of `foo` occurs here
4+
LL | let x = &mut block;
5+
| ---------- mutable borrow occurs here
6+
LL | let p: &'a u8 = &*block.current;
7+
| ^^^^^^^^^^^^^^^ immutable borrow occurs here
8+
LL | // (use `x` and `p` so enabling NLL doesn't assign overly short lifetimes)
9+
LL | drop(x);
10+
| - mutable borrow later used here
1011
|
11-
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
1212
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
1313
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
1414
= note: for more information, try `rustc --explain E0729`

src/test/ui/borrowck/borrowck-mutate-in-guard.nll.stderr

-41
This file was deleted.

src/test/ui/borrowck/borrowck-mutate-in-guard.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,11 @@ fn foo() -> isize {
99
match x {
1010
Enum::A(_) if { x = Enum::B(false); false } => 1,
1111
//~^ ERROR cannot assign in a pattern guard
12-
//~| WARN cannot assign `x` in match guard
13-
//~| WARN this error has been downgraded to a warning for backwards compatibility
14-
//~| WARN this represents potential undefined behavior in your code and this warning will
12+
//~| ERROR cannot assign `x` in match guard
1513
Enum::A(_) if { let y = &mut x; *y = Enum::B(false); false } => 1,
1614
//~^ ERROR cannot mutably borrow in a pattern guard
1715
//~| ERROR cannot assign in a pattern guard
18-
//~| WARN cannot mutably borrow `x` in match guard
19-
//~| WARN this error has been downgraded to a warning for backwards compatibility
20-
//~| WARN this represents potential undefined behavior in your code and this warning will
16+
//~| ERROR cannot mutably borrow `x` in match guard
2117
Enum::A(p) => *p,
2218
Enum::B(_) => 2,
2319
}

src/test/ui/borrowck/borrowck-mutate-in-guard.stderr

+6-14
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,37 @@ LL | Enum::A(_) if { x = Enum::B(false); false } => 1,
55
| ^^^^^^^^^^^^^^^^^^ assignment in pattern guard
66

77
error[E0301]: cannot mutably borrow in a pattern guard
8-
--> $DIR/borrowck-mutate-in-guard.rs:15:38
8+
--> $DIR/borrowck-mutate-in-guard.rs:13:38
99
|
1010
LL | Enum::A(_) if { let y = &mut x; *y = Enum::B(false); false } => 1,
1111
| ^ borrowed mutably in pattern guard
1212
|
1313
= help: add `#![feature(bind_by_move_pattern_guards)]` to the crate attributes to enable
1414

1515
error[E0302]: cannot assign in a pattern guard
16-
--> $DIR/borrowck-mutate-in-guard.rs:15:41
16+
--> $DIR/borrowck-mutate-in-guard.rs:13:41
1717
|
1818
LL | Enum::A(_) if { let y = &mut x; *y = Enum::B(false); false } => 1,
1919
| ^^^^^^^^^^^^^^^^^^^ assignment in pattern guard
2020

21-
warning[E0510]: cannot assign `x` in match guard
21+
error[E0510]: cannot assign `x` in match guard
2222
--> $DIR/borrowck-mutate-in-guard.rs:10:25
2323
|
2424
LL | match x {
2525
| - value is immutable in match guard
2626
LL | Enum::A(_) if { x = Enum::B(false); false } => 1,
2727
| ^^^^^^^^^^^^^^^^^^ cannot assign
28-
|
29-
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
30-
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
31-
= note: for more information, try `rustc --explain E0729`
3228

33-
warning[E0510]: cannot mutably borrow `x` in match guard
34-
--> $DIR/borrowck-mutate-in-guard.rs:15:33
29+
error[E0510]: cannot mutably borrow `x` in match guard
30+
--> $DIR/borrowck-mutate-in-guard.rs:13:33
3531
|
3632
LL | match x {
3733
| - value is immutable in match guard
3834
...
3935
LL | Enum::A(_) if { let y = &mut x; *y = Enum::B(false); false } => 1,
4036
| ^^^^^^ cannot mutably borrow
41-
|
42-
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
43-
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
44-
= note: for more information, try `rustc --explain E0729`
4537

46-
error: aborting due to 3 previous errors
38+
error: aborting due to 5 previous errors
4739

4840
Some errors have detailed explanations: E0301, E0302, E0510.
4941
For more information about an error, try `rustc --explain E0301`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
fn main() {
2+
match Some(&4) {
3+
None => {},
4+
ref mut foo
5+
if {
6+
(|| { let bar = foo; bar.take() })();
7+
//~^ ERROR cannot move out of `foo` in pattern guard
8+
false
9+
} => {},
10+
Some(ref _s) => println!("Note this arm is bogus; the `Some` became `None` in the guard."),
11+
_ => println!("Here is some supposedly unreachable code."),
12+
}
13+
}

0 commit comments

Comments
 (0)