Skip to content

Commit bdb2d65

Browse files
committed
librustc: Ensure that no moves from the inside of @ or & boxes occur. rs=crashing-servo
1 parent e67190a commit bdb2d65

File tree

2 files changed

+121
-26
lines changed

2 files changed

+121
-26
lines changed

src/librustc/middle/check_alt.rs

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,29 @@ fn check_crate(tcx: ty::ctxt, method_map: method_map, crate: @crate) {
4040
tcx.sess.abort_if_errors();
4141
}
4242

43+
fn expr_is_non_moving_lvalue(cx: @AltCheckCtxt, expr: @expr) -> bool {
44+
if !ty::expr_is_lval(cx.tcx, cx.method_map, expr) {
45+
return false;
46+
}
47+
48+
match cx.tcx.value_modes.find(expr.id) {
49+
Some(MoveValue) => return false,
50+
Some(CopyValue) | Some(ReadValue) => return true,
51+
None => {
52+
cx.tcx.sess.span_bug(expr.span, ~"no entry in value mode map");
53+
}
54+
}
55+
}
56+
4357
fn check_expr(cx: @AltCheckCtxt, ex: @expr, &&s: (), v: visit::vt<()>) {
4458
visit::visit_expr(ex, s, v);
4559
match ex.node {
4660
expr_match(scrut, ref arms) => {
4761
// First, check legality of move bindings.
48-
let is_lvalue = ty::expr_is_lval(cx.tcx, cx.method_map, scrut);
62+
let is_non_moving_lvalue = expr_is_non_moving_lvalue(cx, ex);
4963
for arms.each |arm| {
5064
check_legality_of_move_bindings(cx,
51-
is_lvalue,
65+
is_non_moving_lvalue,
5266
arm.guard.is_some(),
5367
arm.pats);
5468
}
@@ -524,7 +538,7 @@ fn check_local(cx: @AltCheckCtxt, loc: @local, &&s: (), v: visit::vt<()>) {
524538

525539
// Check legality of move bindings.
526540
let is_lvalue = match loc.node.init {
527-
Some(init) => ty::expr_is_lval(cx.tcx, cx.method_map, init),
541+
Some(init) => expr_is_non_moving_lvalue(cx, init),
528542
None => true
529543
};
530544
check_legality_of_move_bindings(cx, is_lvalue, false, [ loc.node.pat ]);
@@ -616,40 +630,98 @@ fn check_legality_of_move_bindings(cx: @AltCheckCtxt,
616630
}
617631
}
618632

633+
let check_move: &fn(@pat, Option<@pat>) = |p, sub| {
634+
// check legality of moving out of the enum
635+
if sub.is_some() {
636+
tcx.sess.span_err(
637+
p.span,
638+
~"cannot bind by-move with sub-bindings");
639+
} else if has_guard {
640+
tcx.sess.span_err(
641+
p.span,
642+
~"cannot bind by-move into a pattern guard");
643+
} else if by_ref_span.is_some() {
644+
tcx.sess.span_err(
645+
p.span,
646+
~"cannot bind by-move and by-ref \
647+
in the same pattern");
648+
tcx.sess.span_note(
649+
by_ref_span.get(),
650+
~"by-ref binding occurs here");
651+
} else if is_lvalue {
652+
tcx.sess.span_err(
653+
p.span,
654+
~"cannot bind by-move when \
655+
matching an lvalue");
656+
}
657+
};
658+
619659
if !any_by_move { return; } // pointless micro-optimization
620660
for pats.each |pat| {
621661
do walk_pat(*pat) |p| {
622662
if pat_is_binding(def_map, p) {
623663
match p.node {
624-
pat_ident(bind_by_move, _, sub) => {
625-
// check legality of moving out of the enum
626-
if sub.is_some() {
627-
tcx.sess.span_err(
628-
p.span,
629-
~"cannot bind by-move with sub-bindings");
630-
} else if has_guard {
631-
tcx.sess.span_err(
632-
p.span,
633-
~"cannot bind by-move into a pattern guard");
634-
} else if by_ref_span.is_some() {
635-
tcx.sess.span_err(
636-
p.span,
637-
~"cannot bind by-move and by-ref \
638-
in the same pattern");
639-
tcx.sess.span_note(
640-
by_ref_span.get(),
641-
~"by-ref binding occurs here");
642-
} else if is_lvalue {
643-
tcx.sess.span_err(
644-
p.span,
645-
~"cannot bind by-move when \
646-
matching an lvalue");
664+
pat_ident(bind_by_move, _, sub) => check_move(p, sub),
665+
pat_ident(bind_infer, _, sub) => {
666+
match tcx.value_modes.find(p.id) {
667+
Some(MoveValue) => check_move(p, sub),
668+
Some(CopyValue) | Some(ReadValue) => {}
669+
None => {
670+
cx.tcx.sess.span_bug(
671+
pat.span, ~"no mode for pat binding");
672+
}
647673
}
648674
}
649675
_ => {}
650676
}
651677
}
652678
}
679+
680+
// Now check to ensure that any move binding is not behind an @ or &.
681+
// This is always illegal.
682+
let vt = visit::mk_vt(@{
683+
visit_pat: |pat, behind_bad_pointer, v| {
684+
let error_out = || {
685+
cx.tcx.sess.span_err(pat.span, ~"by-move pattern \
686+
bindings may not occur \
687+
behind @ or & bindings");
688+
};
689+
match pat.node {
690+
pat_ident(binding_mode, _, sub) => {
691+
debug!("(check legality of move) checking pat \
692+
ident with behind_bad_pointer %?",
693+
behind_bad_pointer);
694+
match binding_mode {
695+
bind_by_move if behind_bad_pointer => error_out(),
696+
bind_infer if behind_bad_pointer => {
697+
match cx.tcx.value_modes.find(pat.id) {
698+
Some(MoveValue) => error_out(),
699+
Some(CopyValue) |
700+
Some(ReadValue) => {}
701+
None => {
702+
cx.tcx.sess.span_bug(pat.span,
703+
~"no mode for pat binding");
704+
}
705+
}
706+
}
707+
_ => {}
708+
}
709+
match sub {
710+
None => {}
711+
Some(subpat) => {
712+
(v.visit_pat)(subpat, behind_bad_pointer, v);
713+
}
714+
}
715+
}
716+
pat_box(subpat) | pat_region(subpat) => {
717+
(v.visit_pat)(subpat, true, v);
718+
}
719+
_ => visit::visit_pat(pat, behind_bad_pointer, v)
720+
}
721+
},
722+
.. *visit::default_visitor::<bool>()
723+
});
724+
(vt.visit_pat)(*pat, false, vt);
653725
}
654726
}
655727

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
enum E {
2+
Foo,
3+
Bar(~str)
4+
}
5+
6+
struct S {
7+
x: E
8+
}
9+
10+
fn f(x: ~str) {}
11+
12+
fn main() {
13+
let s = S { x: Bar(~"hello") };
14+
match &s.x {
15+
&Foo => {}
16+
&Bar(identifier) => f(copy identifier) //~ ERROR by-move pattern bindings may not occur
17+
};
18+
match &s.x {
19+
&Foo => {}
20+
&Bar(ref identifier) => io::println(*identifier)
21+
};
22+
}
23+

0 commit comments

Comments
 (0)