Skip to content

Commit 5b25fc9

Browse files
committed
Parse and typecheck moving out of enums (#2329)
1 parent 1b804ce commit 5b25fc9

File tree

9 files changed

+63
-8
lines changed

9 files changed

+63
-8
lines changed

src/libsyntax/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ type field_pat = {ident: ident, pat: @pat};
161161
#[auto_serialize]
162162
enum binding_mode {
163163
bind_by_value,
164+
bind_by_move,
164165
bind_by_ref(ast::mutability),
165166
bind_by_implicit_ref
166167
}

src/libsyntax/parse/parser.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import common::{seq_sep_trailing_disallowed, seq_sep_trailing_allowed,
1616
import dvec::dvec;
1717
import vec::{push};
1818
import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
19-
bind_by_ref, bind_by_implicit_ref, bind_by_value,
19+
bind_by_ref, bind_by_implicit_ref, bind_by_value, bind_by_move,
2020
bitand, bitor, bitxor, blk, blk_check_mode, bound_const,
2121
bound_copy, bound_send, bound_trait, bound_owned, box, by_copy,
2222
by_move, by_mutbl_ref, by_ref, by_val, capture_clause,
@@ -1887,12 +1887,17 @@ struct parser {
18871887
pat = self.parse_pat_ident(refutable, bind_by_ref(mutbl));
18881888
} else if self.eat_keyword(~"copy") {
18891889
pat = self.parse_pat_ident(refutable, bind_by_value);
1890+
} else if self.eat_keyword(~"move") {
1891+
pat = self.parse_pat_ident(refutable, bind_by_move);
18901892
} else if !is_plain_ident(self.token) {
18911893
pat = self.parse_enum_variant(refutable);
18921894
} else {
18931895
let binding_mode;
1896+
// XXX: Aren't these two cases deadcode? -- bblum
18941897
if self.eat_keyword(~"copy") {
18951898
binding_mode = bind_by_value;
1899+
} else if self.eat_keyword(~"move") {
1900+
binding_mode = bind_by_move;
18961901
} else if refutable {
18971902
// XXX: Should be bind_by_value, but that's not
18981903
// backward compatible.

src/libsyntax/print/pprust.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,7 @@ fn print_pat(s: ps, &&pat: @ast::pat) {
14421442
print_mutability(s, mutbl);
14431443
}
14441444
ast::bind_by_implicit_ref |
1445+
ast::bind_by_move | // this is totally the wrong thing
14451446
ast::bind_by_value => {}
14461447
}
14471448
print_path(s, path, true);

src/rustc/middle/borrowck/gather_loans.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,10 @@ impl gather_loan_ctxt {
412412
match pat.node {
413413
ast::pat_ident(bm, id, o_pat) if !self.pat_is_variant(pat) => {
414414
match bm {
415-
ast::bind_by_value => {
415+
ast::bind_by_value | ast::bind_by_move => {
416416
// copying does not borrow anything, so no check
417417
// is required
418+
// as for move, check::alt ensures it's from an rvalue.
418419
}
419420
ast::bind_by_ref(mutbl) => {
420421
// ref x or ref x @ p --- creates a ptr which must

src/rustc/middle/lint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ fn check_pat(tcx: ty::ctxt, pat: @ast::pat) {
490490

491491
do pat_bindings(tcx.def_map, pat) |binding_mode, id, span, path| {
492492
match binding_mode {
493-
ast::bind_by_ref(_) | ast::bind_by_value => {}
493+
ast::bind_by_ref(_) | ast::bind_by_value | ast::bind_by_move => {}
494494
ast::bind_by_implicit_ref => {
495495
let pat_ty = ty::node_id_to_type(tcx, id);
496496
let kind = ty::type_kind(tcx, pat_ty);

src/rustc/middle/mem_categorization.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ impl &mem_categorization_ctxt {
409409
}
410410

411411
ast::def_binding(vid, ast::bind_by_value) |
412+
ast::def_binding(vid, ast::bind_by_move) |
412413
ast::def_binding(vid, ast::bind_by_ref(_)) => {
413414
// by-value/by-ref bindings are local variables
414415
@{id:id, span:span,

src/rustc/middle/trans/alt.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
474474
load_if_immediate(bcx, llval, ty), ty);
475475
bcx.fcx.lllocals.insert(val, local_mem(alloc));
476476
add_clean(bcx, alloc, ty);
477+
} else if mode == ast::bind_by_move {
478+
fail ~"can't translate bind_by_move into a pattern guard";
477479
} else {
478480
bcx.fcx.lllocals.insert(val, local_mem(llval));
479481
}
@@ -803,6 +805,9 @@ fn make_pattern_bindings(bcx: block, phi_bindings: phi_bindings_list)
803805
local_mem(allocation));
804806
add_clean(bcx, allocation, ty);
805807
}
808+
ast::bind_by_move => {
809+
fail ~"unimplemented -- bblum";
810+
}
806811
}
807812
}
808813

src/rustc/middle/typeck/check.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2035,7 +2035,10 @@ fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
20352035
map: pat_id_map(fcx.ccx.tcx.def_map, local.node.pat),
20362036
alt_region: region,
20372037
block_region: region,
2038-
pat_region: region
2038+
pat_region: region,
2039+
matching_lvalue: true, // FIXME(#3235) Make this more flexible
2040+
has_guard: false,
2041+
mut ever_bound_by_ref: false,
20392042
};
20402043
alt::check_pat(pcx, local.node.pat, t);
20412044
return bot;

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

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ fn check_alt(fcx: @fn_ctxt,
99

1010
let pattern_ty = fcx.infcx.next_ty_var();
1111
bot = check_expr_with(fcx, discrim, pattern_ty);
12+
let is_lvalue = ty::expr_is_lval(fcx.ccx.method_map, discrim);
1213

1314
// Typecheck the patterns first, so that we get types for all the
1415
// bindings.
@@ -18,7 +19,12 @@ fn check_alt(fcx: @fn_ctxt,
1819
map: pat_id_map(tcx.def_map, arm.pats[0]),
1920
alt_region: ty::re_scope(expr.id),
2021
block_region: ty::re_scope(arm.body.node.id),
21-
pat_region: ty::re_scope(expr.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,
2228
};
2329

2430
for arm.pats.each |p| { check_pat(pcx, p, pattern_ty);}
@@ -47,7 +53,14 @@ type pat_ctxt = {
4753
alt_region: ty::region,
4854
block_region: ty::region,
4955
/* Equal to either alt_region or block_region. */
50-
pat_region: ty::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,
5164
};
5265

5366
fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
@@ -161,6 +174,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
161174

162175
match bm {
163176
ast::bind_by_ref(mutbl) => {
177+
pcx.ever_bound_by_ref = true;
164178
// if the binding is like
165179
// ref x | ref const x | ref mut x
166180
// then the type of x is &M T where M is the mutability
@@ -172,8 +186,32 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
172186
let region_ty = ty::mk_rptr(tcx, region_var, mt);
173187
demand::eqtype(fcx, pat.span, region_ty, typ);
174188
}
175-
ast::bind_by_value | ast::bind_by_implicit_ref => {
176-
// otherwise the type of x is the expected type T
189+
// otherwise the type of x is the expected type T
190+
ast::bind_by_value => {
191+
demand::eqtype(fcx, pat.span, expected, typ);
192+
}
193+
ast::bind_by_move => {
194+
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+
}
212+
}
213+
ast::bind_by_implicit_ref => {
214+
pcx.ever_bound_by_ref = true;
177215
demand::eqtype(fcx, pat.span, expected, typ);
178216
}
179217
}

0 commit comments

Comments
 (0)