Skip to content

Commit 5b85c8c

Browse files
committed
librustc: Forbid pattern bindings after @s, for memory safety.
This is an alternative to upgrading the way rvalues are handled in the borrow check. Making rvalues handled more like lvalues in the borrow check caused numerous problems related to double mutable borrows and rvalue scopes. Rather than come up with more borrow check rules to try to solve these problems, I decided to just forbid pattern bindings after `@`. This affected fewer than 10 lines of code in the compiler and libraries. This breaks code like: match x { y @ z => { ... } } match a { b @ Some(c) => { ... } } Change this code to use nested `match` or `let` expressions. For example: match x { y => { let z = y; ... } } match a { Some(c) => { let b = Some(c); ... } } Closes rust-lang#14587. [breaking-change]
1 parent 51ff6c0 commit 5b85c8c

File tree

11 files changed

+103
-129
lines changed

11 files changed

+103
-129
lines changed

src/doc/rust.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3309,7 +3309,12 @@ enum List { Nil, Cons(uint, Box<List>) }
33093309
fn is_sorted(list: &List) -> bool {
33103310
match *list {
33113311
Nil | Cons(_, box Nil) => true,
3312-
Cons(x, ref r @ box Cons(y, _)) => (x <= y) && is_sorted(&**r)
3312+
Cons(x, ref r @ box Cons(_, _)) => {
3313+
match *r {
3314+
box Cons(y, _) => (x <= y) && is_sorted(&**r),
3315+
_ => fail!()
3316+
}
3317+
}
33133318
}
33143319
}
33153320

src/libcollections/vec.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1724,7 +1724,7 @@ mod tests {
17241724
}
17251725
}
17261726

1727-
let mut count_x @ mut count_y = 0;
1727+
let (mut count_x, mut count_y) = (0, 0);
17281728
{
17291729
let mut tv = TwoVec {
17301730
x: Vec::new(),

src/librustc/lint/context.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,9 @@ impl LintPass for GatherNodeLevels {
630630
match it.node {
631631
ast::ItemEnum(..) => {
632632
let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCE);
633-
match cx.lints.get_level_source(lint_id) {
634-
lvlsrc @ (lvl, _) if lvl != Allow => {
633+
let lvlsrc = cx.lints.get_level_source(lint_id);
634+
match lvlsrc {
635+
(lvl, _) if lvl != Allow => {
635636
cx.node_levels.borrow_mut()
636637
.insert((it.id, lint_id), lvlsrc);
637638
},

src/librustc/middle/borrowck/gather_loans/restrictions.rs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -139,27 +139,36 @@ impl<'a> RestrictionsContext<'a> {
139139
Safe
140140
}
141141

142-
mc::cat_deref(cmt_base, _, pk @ mc::BorrowedPtr(ty::MutBorrow, lt)) |
143-
mc::cat_deref(cmt_base, _, pk @ mc::Implicit(ty::MutBorrow, lt)) => {
144-
// R-Deref-Mut-Borrowed
145-
if !self.bccx.is_subregion_of(self.loan_region, lt) {
146-
self.bccx.report(
147-
BckError {
148-
span: self.span,
149-
cause: self.cause,
150-
cmt: cmt_base,
151-
code: err_borrowed_pointer_too_short(
152-
self.loan_region, lt)});
153-
return Safe;
142+
mc::cat_deref(cmt_base, _, pk) => {
143+
match pk {
144+
mc::BorrowedPtr(ty::MutBorrow, lt) |
145+
mc::Implicit(ty::MutBorrow, lt) => {
146+
// R-Deref-Mut-Borrowed
147+
if !self.bccx.is_subregion_of(self.loan_region, lt) {
148+
self.bccx.report(
149+
BckError {
150+
span: self.span,
151+
cause: self.cause,
152+
cmt: cmt_base,
153+
code: err_borrowed_pointer_too_short(
154+
self.loan_region, lt)});
155+
return Safe;
156+
}
157+
158+
let result = self.restrict(cmt_base);
159+
self.extend(result, cmt.mutbl, LpDeref(pk))
160+
}
161+
mc::UnsafePtr(..) => {
162+
// We are very trusting when working with unsafe
163+
// pointers.
164+
Safe
165+
}
166+
_ => {
167+
self.bccx.tcx.sess.span_bug(self.span,
168+
"unhandled memcat in \
169+
cat_deref")
170+
}
154171
}
155-
156-
let result = self.restrict(cmt_base);
157-
self.extend(result, cmt.mutbl, LpDeref(pk))
158-
}
159-
160-
mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
161-
// We are very trusting when working with unsafe pointers.
162-
Safe
163172
}
164173

165174
mc::cat_discr(cmt_base, _) => {

src/librustc/middle/check_match.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
145145
check_legality_of_move_bindings(cx,
146146
arm.guard.is_some(),
147147
arm.pats.as_slice());
148+
for pat in arm.pats.iter() {
149+
check_legality_of_bindings_in_at_patterns(cx, &**pat);
150+
}
148151
}
149152

150153
// Second, if there is a guard on each arm, make sure it isn't
@@ -200,6 +203,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
200203

201204
// Check legality of move bindings.
202205
check_legality_of_move_bindings(cx, false, [ *pat ]);
206+
check_legality_of_bindings_in_at_patterns(cx, &**pat);
203207
}
204208
_ => ()
205209
}
@@ -455,8 +459,12 @@ fn all_constructors(cx: &MatchCheckCtxt, left_ty: ty::t,
455459

456460
// Note: is_useful doesn't work on empty types, as the paper notes.
457461
// So it assumes that v is non-empty.
458-
fn is_useful(cx: &MatchCheckCtxt, matrix @ &Matrix(ref rows): &Matrix,
459-
v: &[Gc<Pat>], witness: WitnessPreference) -> Usefulness {
462+
fn is_useful(cx: &MatchCheckCtxt,
463+
matrix: &Matrix,
464+
v: &[Gc<Pat>],
465+
witness: WitnessPreference)
466+
-> Usefulness {
467+
let &Matrix(ref rows) = matrix;
460468
debug!("{:}", matrix);
461469
if rows.len() == 0u {
462470
return match witness {
@@ -819,8 +827,9 @@ fn check_local(cx: &mut MatchCheckCtxt, loc: &Local) {
819827
None => ()
820828
}
821829

822-
// Check legality of move bindings.
830+
// Check legality of move bindings and `@` patterns.
823831
check_legality_of_move_bindings(cx, false, [ loc.pat ]);
832+
check_legality_of_bindings_in_at_patterns(cx, &*loc.pat);
824833
}
825834

826835
fn check_fn(cx: &mut MatchCheckCtxt,
@@ -840,6 +849,7 @@ fn check_fn(cx: &mut MatchCheckCtxt,
840849
None => ()
841850
}
842851
check_legality_of_move_bindings(cx, false, [input.pat]);
852+
check_legality_of_bindings_in_at_patterns(cx, &*input.pat);
843853
}
844854
}
845855

@@ -856,7 +866,6 @@ fn is_refutable(cx: &MatchCheckCtxt, pat: Gc<Pat>) -> Option<Gc<Pat>> {
856866
}
857867

858868
// Legality of move bindings checking
859-
860869
fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
861870
has_guard: bool,
862871
pats: &[Gc<Pat>]) {
@@ -966,3 +975,32 @@ impl<'a> Delegate for MutationChecker<'a> {
966975
}
967976
}
968977

978+
/// Forbids bindings in `@` patterns. This is necessary for memory safety,
979+
/// because of the way rvalues are handled in the borrow check. (See issue
980+
/// #14587.)
981+
fn check_legality_of_bindings_in_at_patterns(cx: &MatchCheckCtxt, pat: &Pat) {
982+
let mut visitor = AtBindingPatternVisitor {
983+
cx: cx,
984+
};
985+
visitor.visit_pat(pat, true);
986+
}
987+
988+
struct AtBindingPatternVisitor<'a,'b> {
989+
cx: &'a MatchCheckCtxt<'b>,
990+
}
991+
992+
impl<'a,'b> Visitor<bool> for AtBindingPatternVisitor<'a,'b> {
993+
fn visit_pat(&mut self, pat: &Pat, bindings_allowed: bool) {
994+
if !bindings_allowed && pat_is_binding(&self.cx.tcx.def_map, pat) {
995+
self.cx.tcx.sess.span_err(pat.span,
996+
"pattern bindings are not allowed \
997+
after an `@`");
998+
}
999+
1000+
match pat.node {
1001+
PatIdent(_, _, Some(_)) => visit::walk_pat(self, pat, false),
1002+
_ => visit::walk_pat(self, pat, bindings_allowed),
1003+
}
1004+
}
1005+
}
1006+

src/librustc/middle/typeck/infer/region_inference/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -600,8 +600,9 @@ impl<'a> RegionVarBindings<'a> {
600600
b).as_slice());
601601
}
602602

603-
(f @ ReFree(ref fr), ReScope(s_id)) |
604-
(ReScope(s_id), f @ ReFree(ref fr)) => {
603+
(ReFree(ref fr), ReScope(s_id)) |
604+
(ReScope(s_id), ReFree(ref fr)) => {
605+
let f = ReFree(*fr);
605606
// A "free" region can be interpreted as "some region
606607
// at least as big as the block fr.scope_id". So, we can
607608
// reasonably compare free regions and scopes:
@@ -706,8 +707,9 @@ impl<'a> RegionVarBindings<'a> {
706707
b).as_slice());
707708
}
708709

709-
(ReFree(ref fr), s @ ReScope(s_id)) |
710-
(s @ ReScope(s_id), ReFree(ref fr)) => {
710+
(ReFree(ref fr), ReScope(s_id)) |
711+
(ReScope(s_id), ReFree(ref fr)) => {
712+
let s = ReScope(s_id);
711713
// Free region is something "at least as big as
712714
// `fr.scope_id`." If we find that the scope `fr.scope_id` is bigger
713715
// than the scope `s_id`, then we can say that the GLB

src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-1.rs

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/test/compile-fail/bind-by-move-no-sub-bindings-fun-args.rs

Lines changed: 0 additions & 21 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -8,18 +8,19 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
struct X { x: (), }
12-
13-
impl Drop for X {
14-
fn drop(&mut self) {
15-
println!("destructor runs");
16-
}
11+
enum Option<T> {
12+
None,
13+
Some(T),
1714
}
1815

1916
fn main() {
20-
let x = Some(X { x: () });
21-
match x {
22-
Some(_y @ ref _z) => { }, //~ ERROR cannot bind by-move with sub-bindings
23-
None => fail!()
17+
match &mut Some(1i) {
18+
ref mut z @ &Some(ref a) => {
19+
//~^ ERROR pattern bindings are not allowed after an `@`
20+
**z = None;
21+
println!("{}", *a);
22+
}
23+
_ => ()
2424
}
2525
}
26+

src/test/run-pass/match-pattern-bindings.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,15 @@ fn main() {
1515
ref b @ None => b
1616
}, &Some(1i));
1717
assert_eq!(match value {
18-
ref a @ ref _c @ Some(_) => a,
19-
ref b @ None => b
20-
}, &Some(1i));
21-
assert_eq!(match value {
22-
_a @ ref c @ Some(_) => c,
18+
ref c @ Some(_) => c,
2319
ref b @ None => b
2420
}, &Some(1i));
2521
assert_eq!(match "foobarbaz" {
26-
_a @ b @ _ => b
22+
b @ _ => b
2723
}, "foobarbaz");
28-
29-
let a @ b @ c = "foobarbaz";
24+
let a @ _ = "foobarbaz";
3025
assert_eq!(a, "foobarbaz");
31-
assert_eq!(b, "foobarbaz");
32-
assert_eq!(c, "foobarbaz");
3326
let value = Some(true);
34-
let ref a @ b @ ref c = value;
27+
let ref a @ _ = value;
3528
assert_eq!(a, &Some(true));
36-
assert_eq!(b, Some(true));
37-
assert_eq!(c, &Some(true));
3829
}

src/test/run-pass/nested-patterns.rs

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)