Skip to content

Commit b01d21b

Browse files
committed
report illegal move/ref combos whether or not ref comes first
1 parent a19dce6 commit b01d21b

File tree

3 files changed

+94
-45
lines changed

3 files changed

+94
-45
lines changed

src/rustc/middle/typeck/check.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,11 +2037,16 @@ fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
20372037

20382038
let t = ty::mk_var(fcx.ccx.tcx, fcx.locals.get(local.node.id));
20392039
fcx.write_ty(local.node.id, t);
2040+
2041+
let is_lvalue;
20402042
match local.node.init {
2041-
Some(init) => {
2042-
bot = check_decl_initializer(fcx, local.node.id, init);
2043-
}
2044-
_ => {/* fall through */ }
2043+
Some(init) => {
2044+
is_lvalue = ty::expr_is_lval(fcx.ccx.method_map, init.expr);
2045+
bot = check_decl_initializer(fcx, local.node.id, init);
2046+
}
2047+
_ => {
2048+
is_lvalue = true;
2049+
}
20452050
}
20462051

20472052
let region =
@@ -2051,12 +2056,13 @@ fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
20512056
map: pat_id_map(fcx.ccx.tcx.def_map, local.node.pat),
20522057
alt_region: region,
20532058
block_region: region,
2054-
pat_region: region,
2055-
matching_lvalue: true, // FIXME(#3235) Make this more flexible
2056-
has_guard: false,
2057-
mut ever_bound_by_ref: false,
20582059
};
20592060
alt::check_pat(pcx, local.node.pat, t);
2061+
let has_guard = false;
2062+
alt::check_legality_of_move_bindings(fcx,
2063+
is_lvalue,
2064+
has_guard,
2065+
[local.node.pat]);
20602066
return bot;
20612067
}
20622068

src/rustc/middle/typeck/check/alt.rs

Lines changed: 71 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import syntax::print::pprust;
2+
import syntax::ast_util::{walk_pat};
3+
import pat_util::{pat_is_variant};
24

35
fn check_alt(fcx: @fn_ctxt,
46
expr: @ast::expr,
@@ -18,17 +20,16 @@ fn check_alt(fcx: @fn_ctxt,
1820
fcx: fcx,
1921
map: pat_id_map(tcx.def_map, arm.pats[0]),
2022
alt_region: ty::re_scope(expr.id),
21-
block_region: ty::re_scope(arm.body.node.id),
22-
pat_region: ty::re_scope(expr.id),
23-
// The following three fields determine whether 'move' is allowed.
24-
matching_lvalue: is_lvalue,
25-
has_guard: arm.guard.is_some(),
26-
// Each arm is freshly allowed to decide whether it can 'move'.
27-
mut ever_bound_by_ref: false,
23+
block_region: ty::re_scope(arm.body.node.id)
2824
};
2925

3026
for arm.pats.each |p| { check_pat(pcx, p, pattern_ty);}
27+
check_legality_of_move_bindings(fcx,
28+
is_lvalue,
29+
arm.guard.is_some(),
30+
arm.pats);
3131
}
32+
3233
// Now typecheck the blocks.
3334
let mut result_ty = fcx.infcx.next_ty_var();
3435
let mut arm_non_bot = false;
@@ -47,20 +48,72 @@ fn check_alt(fcx: @fn_ctxt,
4748
return bot;
4849
}
4950

51+
fn check_legality_of_move_bindings(fcx: @fn_ctxt,
52+
is_lvalue: bool,
53+
has_guard: bool,
54+
pats: &[@ast::pat])
55+
{
56+
let tcx = fcx.tcx();
57+
let def_map = tcx.def_map;
58+
let mut by_ref = None;
59+
let mut any_by_move = false;
60+
for pats.each |pat| {
61+
do pat_util::pat_bindings(def_map, pat) |bm, _id, span, _path| {
62+
match bm {
63+
ast::bind_by_ref(_) | ast::bind_by_implicit_ref => {
64+
by_ref = Some(span);
65+
}
66+
ast::bind_by_move => {
67+
any_by_move = true;
68+
}
69+
_ => { }
70+
}
71+
}
72+
}
73+
74+
if !any_by_move { return; } // pointless micro-optimization
75+
for pats.each |pat| {
76+
do walk_pat(pat) |p| {
77+
if !pat_is_variant(def_map, p) {
78+
match p.node {
79+
ast::pat_ident(ast::bind_by_move, _, sub) => {
80+
// check legality of moving out of the enum
81+
if sub.is_some() {
82+
tcx.sess.span_err(
83+
p.span,
84+
~"cannot bind by-move with sub-bindings");
85+
} else if has_guard {
86+
tcx.sess.span_err(
87+
p.span,
88+
~"cannot bind by-move into a pattern guard");
89+
} else if by_ref.is_some() {
90+
tcx.sess.span_err(
91+
p.span,
92+
~"cannot bind by-move and by-ref \
93+
in the same pattern");
94+
tcx.sess.span_note(
95+
by_ref.get(),
96+
~"by-ref binding occurs here");
97+
} else if is_lvalue {
98+
tcx.sess.span_err(
99+
p.span,
100+
~"cannot bind by-move when \
101+
matching an lvalue");
102+
}
103+
}
104+
_ => {}
105+
}
106+
}
107+
}
108+
}
109+
}
110+
111+
50112
type pat_ctxt = {
51113
fcx: @fn_ctxt,
52114
map: pat_id_map,
53-
alt_region: ty::region,
54-
block_region: ty::region,
55-
/* Equal to either alt_region or block_region. */
56-
pat_region: ty::region,
57-
/* Moving out is only permitted when matching rvalues. */
58-
matching_lvalue: bool,
59-
/* Moving out is not permitted with guards. */
60-
has_guard: bool,
61-
/* If a pattern binding binds by-reference ever, then binding by-move in
62-
* the same arm is disallowed (no "ref x @ some(move y)", etc etc). */
63-
mut ever_bound_by_ref: bool,
115+
alt_region: ty::region, // Region for the alt as a whole
116+
block_region: ty::region, // Region for the block of the arm
64117
};
65118

66119
fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
@@ -174,7 +227,6 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
174227
175228
match bm {
176229
ast::bind_by_ref(mutbl) => {
177-
pcx.ever_bound_by_ref = true;
178230
// if the binding is like
179231
// ref x | ref const x | ref mut x
180232
// then the type of x is &M T where M is the mutability
@@ -192,26 +244,8 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
192244
}
193245
ast::bind_by_move => {
194246
demand::eqtype(fcx, pat.span, expected, typ);
195-
// check legality of moving out of the enum
196-
if sub.is_some() {
197-
tcx.sess.span_err(pat.span,
198-
~"cannot bind by-move with sub-bindings");
199-
}
200-
if pcx.has_guard {
201-
tcx.sess.span_err(pat.span,
202-
~"cannot bind by-move into a pattern guard");
203-
}
204-
if pcx.ever_bound_by_ref {
205-
tcx.sess.span_err(pat.span,
206-
~"cannot bind by-move and by-ref in the same pattern");
207-
}
208-
if pcx.matching_lvalue {
209-
tcx.sess.span_err(pat.span,
210-
~"cannot bind by-move when matching an lvalue");
211-
}
212247
}
213248
ast::bind_by_implicit_ref => {
214-
pcx.ever_bound_by_ref = true;
215249
demand::eqtype(fcx, pat.span, expected, typ);
216250
}
217251
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
struct X { x: (); drop { error!("destructor runs"); } }
2+
3+
fn main() {
4+
let x = some((X { x: () }, X { x: () }));
5+
match move x {
6+
some((move _y, ref _z)) => { }, //~ ERROR cannot bind by-move and by-ref in the same pattern
7+
none => fail
8+
}
9+
}

0 commit comments

Comments
 (0)