Skip to content

Commit 5851d32

Browse files
committed
Move checking for moves and initialization of local variables and patterns into
borrow checker and generalize what moves are allowed. Fixes a nasty bug or two in the pattern move checking code. Unifies dataflow code used for initialization and other things. First step towards once fns. Everybody wins. Fixes #4384. Fixes #4715. cc once fns (#2202), optimizing local moves (#5016).
1 parent 5676056 commit 5851d32

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1282
-614
lines changed

src/librust/rust.rc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,15 @@ fn cmd_help(args: &[~str]) -> ValidUsage {
152152
}
153153

154154
match args {
155-
[command_string] => print_usage(command_string),
156-
_ => Invalid
155+
[ref command_string] => print_usage(copy *command_string),
156+
_ => Invalid
157157
}
158158
}
159159

160160
fn cmd_test(args: &[~str]) -> ValidUsage {
161161
match args {
162-
[filename] => {
163-
let test_exec = Path(filename).filestem().unwrap() + "test~";
162+
[ref filename] => {
163+
let test_exec = Path(*filename).filestem().unwrap() + "test~";
164164
invoke("rustc", &[~"--test", filename.to_owned(),
165165
~"-o", test_exec.to_owned()], rustc::main);
166166
let exit_code = run::process_status(~"./" + test_exec, []);
@@ -172,8 +172,8 @@ fn cmd_test(args: &[~str]) -> ValidUsage {
172172

173173
fn cmd_run(args: &[~str]) -> ValidUsage {
174174
match args {
175-
[filename, ..prog_args] => {
176-
let exec = Path(filename).filestem().unwrap() + "~";
175+
[ref filename, ..prog_args] => {
176+
let exec = Path(*filename).filestem().unwrap() + "~";
177177
invoke("rustc", &[filename.to_owned(), ~"-o", exec.to_owned()],
178178
rustc::main);
179179
let exit_code = run::process_status(~"./"+exec, prog_args);

src/librustc/driver/driver.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,8 @@ pub fn compile_rest(sess: Session,
263263
time(time_passes, ~"loop checking", ||
264264
middle::check_loop::check_crate(ty_cx, crate));
265265

266-
let middle::moves::MoveMaps {moves_map, variable_moves_map,
267-
moved_variables_set, capture_map} =
266+
let middle::moves::MoveMaps {moves_map, moved_variables_set,
267+
capture_map} =
268268
time(time_passes, ~"compute moves", ||
269269
middle::moves::compute_moves(ty_cx, method_map, crate));
270270

@@ -274,7 +274,6 @@ pub fn compile_rest(sess: Session,
274274

275275
time(time_passes, ~"liveness checking", ||
276276
middle::liveness::check_crate(ty_cx, method_map,
277-
variable_moves_map,
278277
capture_map, crate));
279278

280279
let (root_map, write_guard_map) =

src/librustc/middle/borrowck/check_loans.rs

Lines changed: 115 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,23 @@ use syntax::codemap::span;
3333

3434
struct CheckLoanCtxt<'self> {
3535
bccx: @BorrowckCtxt,
36-
dfcx: &'self LoanDataFlow,
36+
dfcx_loans: &'self LoanDataFlow,
37+
move_data: move_data::FlowedMoveData,
3738
all_loans: &'self [Loan],
3839
reported: @mut HashSet<ast::node_id>,
3940
}
4041

4142
pub fn check_loans(bccx: @BorrowckCtxt,
42-
dfcx: &LoanDataFlow,
43+
dfcx_loans: &LoanDataFlow,
44+
move_data: move_data::FlowedMoveData,
4345
all_loans: &[Loan],
4446
body: &ast::blk) {
4547
debug!("check_loans(body id=%?)", body.node.id);
4648

4749
let clcx = @mut CheckLoanCtxt {
4850
bccx: bccx,
49-
dfcx: dfcx,
51+
dfcx_loans: dfcx_loans,
52+
move_data: move_data,
5053
all_loans: all_loans,
5154
reported: @mut HashSet::new(),
5255
};
@@ -62,7 +65,6 @@ pub fn check_loans(bccx: @BorrowckCtxt,
6265

6366
enum MoveError {
6467
MoveOk,
65-
MoveFromIllegalCmt(mc::cmt),
6668
MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/span)
6769
}
6870

@@ -79,7 +81,7 @@ pub impl<'self> CheckLoanCtxt<'self> {
7981
//! are issued for future scopes and thus they may have been
8082
//! *issued* but not yet be in effect.
8183
82-
for self.dfcx.each_bit_on_entry(scope_id) |loan_index| {
84+
for self.dfcx_loans.each_bit_on_entry(scope_id) |loan_index| {
8385
let loan = &self.all_loans[loan_index];
8486
if !op(loan) {
8587
return false;
@@ -131,7 +133,7 @@ pub impl<'self> CheckLoanCtxt<'self> {
131133
//! we encounter `scope_id`.
132134
133135
let mut result = ~[];
134-
for self.dfcx.each_gen_bit(scope_id) |loan_index| {
136+
for self.dfcx_loans.each_gen_bit(scope_id) |loan_index| {
135137
result.push(loan_index);
136138
}
137139
return result;
@@ -251,6 +253,29 @@ pub impl<'self> CheckLoanCtxt<'self> {
251253
}
252254
}
253255

256+
fn check_if_path_is_moved(&self,
257+
id: ast::node_id,
258+
span: span,
259+
use_kind: MovedValueUseKind,
260+
lp: @LoanPath) {
261+
/*!
262+
* Reports an error if `expr` (which should be a path)
263+
* is using a moved/uninitialized value
264+
*/
265+
266+
debug!("check_if_path_is_moved(id=%?, use_kind=%?, lp=%s)",
267+
id, use_kind, lp.repr(self.bccx.tcx));
268+
for self.move_data.each_move_of(id, lp) |move, moved_lp| {
269+
self.bccx.report_use_of_moved_value(
270+
span,
271+
use_kind,
272+
lp,
273+
move,
274+
moved_lp);
275+
return;
276+
}
277+
}
278+
254279
fn check_assignment(&self, expr: @ast::expr) {
255280
// We don't use cat_expr() here because we don't want to treat
256281
// auto-ref'd parameters in overloaded operators as rvalues.
@@ -261,48 +286,42 @@ pub impl<'self> CheckLoanCtxt<'self> {
261286

262287
debug!("check_assignment(cmt=%s)", cmt.repr(self.tcx()));
263288

264-
// check that the value being assigned is declared as mutable
265-
// and report an error otherwise.
266-
match cmt.mutbl {
267-
mc::McDeclared => {
268-
// OK, but we have to mark arguments as requiring mut
269-
// if they are assigned (other cases are handled by liveness,
270-
// since we need to distinguish local variables assigned
271-
// once vs those assigned multiple times)
272-
match cmt.cat {
273-
mc::cat_self(*) |
274-
mc::cat_arg(*) => {
275-
mark_variable_as_used_mut(self, cmt);
276-
}
277-
_ => {}
289+
// Mutable values can be assigned, as long as they obey loans
290+
// and aliasing restrictions:
291+
if cmt.mutbl.is_mutable() {
292+
if check_for_aliasable_mutable_writes(self, expr, cmt) {
293+
if check_for_assignment_to_restricted_or_frozen_location(
294+
self, expr, cmt)
295+
{
296+
// Safe, but record for lint pass later:
297+
mark_variable_as_used_mut(self, cmt);
278298
}
279299
}
280-
mc::McInherited => {
281-
// OK, but we may have to add an entry to `used_mut_nodes`
282-
mark_variable_as_used_mut(self, cmt);
283-
}
284-
mc::McReadOnly | mc::McImmutable => {
285-
// Subtle: liveness guarantees that immutable local
286-
// variables are only assigned once, so no need to
287-
// report an error for an assignment to a local
288-
// variable (note also that it is not legal to borrow
289-
// for a local variable before it has been assigned
290-
// for the first time).
291-
if !self.is_local_variable(cmt) {
292-
self.bccx.span_err(
293-
expr.span,
294-
fmt!("cannot assign to %s %s"
295-
cmt.mutbl.to_user_str(),
296-
self.bccx.cmt_to_str(cmt)));
297-
}
300+
return;
301+
}
302+
303+
// For immutable local variables, assignments are legal
304+
// if they cannot already have been assigned
305+
if self.is_local_variable(cmt) {
306+
assert!(cmt.mutbl.is_immutable()); // no "const" locals
307+
let lp = opt_loan_path(cmt).get();
308+
for self.move_data.each_assignment_of(expr.id, lp) |assign| {
309+
self.bccx.report_reassigned_immutable_variable(
310+
expr.span,
311+
lp,
312+
assign);
298313
return;
299314
}
315+
return;
300316
}
301317

302-
if check_for_aliasable_mutable_writes(self, expr, cmt) {
303-
check_for_assignment_to_restricted_or_frozen_location(
304-
self, expr, cmt);
305-
}
318+
// Otherwise, just a plain error.
319+
self.bccx.span_err(
320+
expr.span,
321+
fmt!("cannot assign to %s %s"
322+
cmt.mutbl.to_user_str(),
323+
self.bccx.cmt_to_str(cmt)));
324+
return;
306325

307326
fn mark_variable_as_used_mut(this: &CheckLoanCtxt,
308327
cmt: mc::cmt) {
@@ -538,12 +557,6 @@ pub impl<'self> CheckLoanCtxt<'self> {
538557
let cmt = self.bccx.cat_expr(ex);
539558
match self.analyze_move_out_from_cmt(cmt) {
540559
MoveOk => {}
541-
MoveFromIllegalCmt(_) => {
542-
self.bccx.span_err(
543-
cmt.span,
544-
fmt!("cannot move out of %s",
545-
self.bccx.cmt_to_str(cmt)));
546-
}
547560
MoveWhileBorrowed(loan_path, loan_span) => {
548561
self.bccx.span_err(
549562
cmt.span,
@@ -561,29 +574,7 @@ pub impl<'self> CheckLoanCtxt<'self> {
561574
}
562575

563576
fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError {
564-
debug!("check_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
565-
566-
match cmt.cat {
567-
// Rvalues, locals, and arguments can be moved:
568-
mc::cat_rvalue | mc::cat_local(_) |
569-
mc::cat_arg(_) | mc::cat_self(_) => {}
570-
571-
// It seems strange to allow a move out of a static item,
572-
// but what happens in practice is that you have a
573-
// reference to a constant with a type that should be
574-
// moved, like `None::<~int>`. The type of this constant
575-
// is technically `Option<~int>`, which moves, but we know
576-
// that the content of static items will never actually
577-
// contain allocated pointers, so we can just memcpy it.
578-
mc::cat_static_item => {}
579-
580-
mc::cat_deref(_, _, mc::unsafe_ptr(*)) => {}
581-
582-
// Nothing else.
583-
_ => {
584-
return MoveFromIllegalCmt(cmt);
585-
}
586-
}
577+
debug!("analyze_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
587578

588579
// FIXME(#4384) inadequare if/when we permit `move a.b`
589580

@@ -631,54 +622,53 @@ fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
631622

632623
visit::fk_anon(*) |
633624
visit::fk_fn_block(*) => {
634-
let fty = ty::node_id_to_type(this.tcx(), id);
635-
let fty_sigil = ty::ty_closure_sigil(fty);
636-
check_moves_from_captured_variables(this, id, fty_sigil);
625+
check_captured_variables(this, id, sp);
637626
}
638627
}
639628

640629
visit::visit_fn(fk, decl, body, sp, id, this, visitor);
641630

642-
fn check_moves_from_captured_variables(this: @mut CheckLoanCtxt,
643-
id: ast::node_id,
644-
fty_sigil: ast::Sigil) {
645-
match fty_sigil {
646-
ast::ManagedSigil | ast::OwnedSigil => {
647-
let cap_vars = this.bccx.capture_map.get(&id);
648-
for cap_vars.each |cap_var| {
649-
match cap_var.mode {
650-
moves::CapRef | moves::CapCopy => { loop; }
651-
moves::CapMove => { }
652-
}
653-
let def_id = ast_util::def_id_of_def(cap_var.def).node;
654-
let ty = ty::node_id_to_type(this.tcx(), def_id);
655-
let cmt = this.bccx.cat_def(id, cap_var.span,
656-
ty, cap_var.def);
657-
let move_err = this.analyze_move_out_from_cmt(cmt);
658-
match move_err {
659-
MoveOk => {}
660-
MoveFromIllegalCmt(move_cmt) => {
661-
this.bccx.span_err(
662-
cap_var.span,
663-
fmt!("illegal by-move capture of %s",
664-
this.bccx.cmt_to_str(move_cmt)));
665-
}
666-
MoveWhileBorrowed(loan_path, loan_span) => {
667-
this.bccx.span_err(
668-
cap_var.span,
669-
fmt!("cannot move `%s` into closure \
670-
because it is borrowed",
671-
this.bccx.loan_path_to_str(loan_path)));
672-
this.bccx.span_note(
673-
loan_span,
674-
fmt!("borrow of `%s` occurs here",
675-
this.bccx.loan_path_to_str(loan_path)));
676-
}
677-
}
631+
fn check_captured_variables(this: @mut CheckLoanCtxt,
632+
closure_id: ast::node_id,
633+
span: span) {
634+
let cap_vars = this.bccx.capture_map.get(&closure_id);
635+
for cap_vars.each |cap_var| {
636+
match cap_var.mode {
637+
moves::CapRef | moves::CapCopy => {
638+
let var_id = ast_util::def_id_of_def(cap_var.def).node;
639+
let lp = @LpVar(var_id);
640+
this.check_if_path_is_moved(closure_id, span,
641+
MovedInCapture, lp);
642+
}
643+
moves::CapMove => {
644+
check_by_move_capture(this, closure_id, cap_var);
645+
}
646+
}
647+
}
648+
return;
649+
650+
fn check_by_move_capture(this: @mut CheckLoanCtxt,
651+
closure_id: ast::node_id,
652+
cap_var: &moves::CaptureVar) {
653+
let var_id = ast_util::def_id_of_def(cap_var.def).node;
654+
let ty = ty::node_id_to_type(this.tcx(), var_id);
655+
let cmt = this.bccx.cat_def(closure_id, cap_var.span,
656+
ty, cap_var.def);
657+
let move_err = this.analyze_move_out_from_cmt(cmt);
658+
match move_err {
659+
MoveOk => {}
660+
MoveWhileBorrowed(loan_path, loan_span) => {
661+
this.bccx.span_err(
662+
cap_var.span,
663+
fmt!("cannot move `%s` into closure \
664+
because it is borrowed",
665+
this.bccx.loan_path_to_str(loan_path)));
666+
this.bccx.span_note(
667+
loan_span,
668+
fmt!("borrow of `%s` occurs here",
669+
this.bccx.loan_path_to_str(loan_path)));
678670
}
679671
}
680-
681-
ast::BorrowedSigil => {}
682672
}
683673
}
684674
}
@@ -692,18 +682,29 @@ fn check_loans_in_local<'a>(local: @ast::local,
692682
fn check_loans_in_expr<'a>(expr: @ast::expr,
693683
this: @mut CheckLoanCtxt<'a>,
694684
vt: visit::vt<@mut CheckLoanCtxt<'a>>) {
685+
visit::visit_expr(expr, this, vt);
686+
695687
debug!("check_loans_in_expr(expr=%s)",
696688
expr.repr(this.tcx()));
697689

698-
visit::visit_expr(expr, this, vt);
699-
700690
this.check_for_conflicting_loans(expr.id);
701691

702692
if this.bccx.moves_map.contains(&expr.id) {
703693
this.check_move_out_from_expr(expr);
704694
}
705695

706696
match expr.node {
697+
ast::expr_self |
698+
ast::expr_path(*) => {
699+
if !this.move_data.is_assignee(expr.id) {
700+
let cmt = this.bccx.cat_expr_unadjusted(expr);
701+
debug!("path cmt=%s", cmt.repr(this.tcx()));
702+
for opt_loan_path(cmt).each |&lp| {
703+
this.check_if_path_is_moved(expr.id, expr.span,
704+
MovedInUse, lp);
705+
}
706+
}
707+
}
707708
ast::expr_assign(dest, _) |
708709
ast::expr_assign_op(_, dest, _) => {
709710
this.check_assignment(dest);

0 commit comments

Comments
 (0)