Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 00a6797

Browse files
committedAug 16, 2017
Auto merge of #43108 - pnkfelix:mir-borrowck3c, r=arielb1
MIR borrow check (under debug flag) Here is the current state of MIR borrow check. It consists of (1.) some refactoring, (2.) a dataflow analysis to identify the borrows themselves, and (3.) a mir "transform" that does the borrow check itself based on the aforementioned dataflow results. (There's also a drive-by fix to dataflow that I can factor into a separate PR if necessary. Interestingly I could not find a way to observe the bug outside of MIR borrowck.) To be clear, this branch is not ready to be used as the default borrow check. Thus the code is guarded: To get mir-borrowck to run, you need to either supply an attribute `#[rustc_mir_borrowck]` or a debug flag `-Z borrowck-mir`. Here are the main issues with the current MIR borrowck as it stands in this PR: * No Notes emitted yet, just errors. (So the feedback is definitely inferior compared to AST borrowck today) * Lvalue rendering differs between Ast and Mir. (Mostly minor, but replacement of field names with indices is very bad; big priority for me to fix ASAP.) * Lots of ICEs (presumably because some MIR operations used here have well-formedness assumptions that are violated in borrowck-broken code) * Conflates lots of cases that are distinguished by AST-borrowck * Conflates "uninitialized" with "moved" (special case of previous bullet, one that I think should be fixed ASAP) (I am hoping to fix as many of the above issues as I can in the near term, but I also would like to land this even if they are *not* all fixed, because the rebasing effort is getting to be a real drag.)
2 parents c886246 + 8738a08 commit 00a6797

File tree

22 files changed

+2940
-1098
lines changed

22 files changed

+2940
-1098
lines changed
 

‎src/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/librustc/session/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
918918
"when debug-printing compiler state, do not include spans"), // o/w tests have closure@path
919919
identify_regions: bool = (false, parse_bool, [UNTRACKED],
920920
"make unnamed regions display as '# (where # is some non-ident unique id)"),
921+
borrowck_mir: bool = (false, parse_bool, [UNTRACKED],
922+
"implicitly treat functions as if they have `#[rustc_mir_borrowck]` attribute"),
921923
time_passes: bool = (false, parse_bool, [UNTRACKED],
922924
"measure time of each rustc pass"),
923925
count_llvm_insns: bool = (false, parse_bool,

‎src/librustc_borrowck/borrowck/check_loans.rs

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use rustc::ty::{self, TyCtxt};
2929
use syntax::ast;
3030
use syntax_pos::Span;
3131
use rustc::hir;
32+
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
3233

3334
use std::rc::Rc;
3435

@@ -465,10 +466,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
465466

466467
let mut err = match (new_loan.kind, old_loan.kind) {
467468
(ty::MutBorrow, ty::MutBorrow) => {
468-
let mut err = struct_span_err!(self.bccx, new_loan.span, E0499,
469-
"cannot borrow `{}`{} as mutable \
470-
more than once at a time",
471-
nl, new_loan_msg);
469+
let mut err = self.bccx.cannot_mutably_borrow_multiply(
470+
new_loan.span, &nl, &new_loan_msg, Origin::Ast);
472471

473472
if new_loan.span == old_loan.span {
474473
// Both borrows are happening in the same place
@@ -496,10 +495,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
496495
}
497496

498497
(ty::UniqueImmBorrow, ty::UniqueImmBorrow) => {
499-
let mut err = struct_span_err!(self.bccx, new_loan.span, E0524,
500-
"two closures require unique access to `{}` \
501-
at the same time",
502-
nl);
498+
let mut err = self.bccx.cannot_uniquely_borrow_by_two_closures(
499+
new_loan.span, &nl, Origin::Ast);
503500
err.span_label(
504501
old_loan.span,
505502
"first closure is constructed here");
@@ -513,10 +510,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
513510
}
514511

515512
(ty::UniqueImmBorrow, _) => {
516-
let mut err = struct_span_err!(self.bccx, new_loan.span, E0500,
517-
"closure requires unique access to `{}` \
518-
but {} is already borrowed{}",
519-
nl, ol_pronoun, old_loan_msg);
513+
let mut err = self.bccx.cannot_uniquely_borrow_by_one_closure(
514+
new_loan.span, &nl, &ol_pronoun, &old_loan_msg, Origin::Ast);
520515
err.span_label(
521516
new_loan.span,
522517
format!("closure construction occurs here{}", new_loan_msg));
@@ -530,10 +525,9 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
530525
}
531526

532527
(_, ty::UniqueImmBorrow) => {
533-
let mut err = struct_span_err!(self.bccx, new_loan.span, E0501,
534-
"cannot borrow `{}`{} as {} because \
535-
previous closure requires unique access",
536-
nl, new_loan_msg, new_loan.kind.to_user_str());
528+
let new_loan_str = &new_loan.kind.to_user_str();
529+
let mut err = self.bccx.cannot_reborrow_already_uniquely_borrowed(
530+
new_loan.span, &nl, &new_loan_msg, new_loan_str, Origin::Ast);
537531
err.span_label(
538532
new_loan.span,
539533
format!("borrow occurs here{}", new_loan_msg));
@@ -547,15 +541,10 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
547541
}
548542

549543
(..) => {
550-
let mut err = struct_span_err!(self.bccx, new_loan.span, E0502,
551-
"cannot borrow `{}`{} as {} because \
552-
{} is also borrowed as {}{}",
553-
nl,
554-
new_loan_msg,
555-
new_loan.kind.to_user_str(),
556-
ol_pronoun,
557-
old_loan.kind.to_user_str(),
558-
old_loan_msg);
544+
let mut err = self.bccx.cannot_reborrow_already_borrowed(
545+
new_loan.span,
546+
&nl, &new_loan_msg, &new_loan.kind.to_user_str(),
547+
&ol_pronoun, &old_loan.kind.to_user_str(), &old_loan_msg, Origin::Ast);
559548
err.span_label(
560549
new_loan.span,
561550
format!("{} borrow occurs here{}",
@@ -645,9 +634,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
645634
match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
646635
UseOk => { }
647636
UseWhileBorrowed(loan_path, loan_span) => {
648-
struct_span_err!(self.bccx, span, E0503,
649-
"cannot use `{}` because it was mutably borrowed",
650-
&self.bccx.loan_path_to_string(copy_path))
637+
let desc = self.bccx.loan_path_to_string(copy_path);
638+
self.bccx.cannot_use_when_mutably_borrowed(span, &desc, Origin::Ast)
651639
.span_label(loan_span,
652640
format!("borrow of `{}` occurs here",
653641
&self.bccx.loan_path_to_string(&loan_path))
@@ -673,9 +661,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
673661
UseWhileBorrowed(loan_path, loan_span) => {
674662
let mut err = match move_kind {
675663
move_data::Captured => {
676-
let mut err = struct_span_err!(self.bccx, span, E0504,
677-
"cannot move `{}` into closure because it is borrowed",
678-
&self.bccx.loan_path_to_string(move_path));
664+
let mut err = self.bccx.cannot_move_into_closure(
665+
span, &self.bccx.loan_path_to_string(move_path), Origin::Ast);
679666
err.span_label(
680667
loan_span,
681668
format!("borrow of `{}` occurs here",
@@ -690,9 +677,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
690677
move_data::Declared |
691678
move_data::MoveExpr |
692679
move_data::MovePat => {
693-
let mut err = struct_span_err!(self.bccx, span, E0505,
694-
"cannot move out of `{}` because it is borrowed",
695-
&self.bccx.loan_path_to_string(move_path));
680+
let desc = self.bccx.loan_path_to_string(move_path);
681+
let mut err = self.bccx.cannot_move_when_borrowed(span, &desc, Origin::Ast);
696682
err.span_label(
697683
loan_span,
698684
format!("borrow of `{}` occurs here",
@@ -874,9 +860,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
874860
span: Span,
875861
loan_path: &LoanPath<'tcx>,
876862
loan: &Loan) {
877-
struct_span_err!(self.bccx, span, E0506,
878-
"cannot assign to `{}` because it is borrowed",
879-
self.bccx.loan_path_to_string(loan_path))
863+
self.bccx.cannot_assign_to_borrowed(
864+
span, &self.bccx.loan_path_to_string(loan_path), Origin::Ast)
880865
.span_label(loan.span,
881866
format!("borrow of `{}` occurs here",
882867
self.bccx.loan_path_to_string(loan_path)))

‎src/librustc_borrowck/borrowck/mod.rs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ use rustc::middle::free_region::RegionRelations;
3737
use rustc::ty::{self, TyCtxt};
3838
use rustc::ty::maps::Providers;
3939

40+
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
41+
4042
use std::fmt;
4143
use std::rc::Rc;
4244
use std::hash::{Hash, Hasher};
@@ -218,6 +220,25 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
218220
owner_def_id: DefId,
219221
}
220222

223+
impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> {
224+
fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
225+
sp: S,
226+
msg: &str,
227+
code: &str)
228+
-> DiagnosticBuilder<'a>
229+
{
230+
self.tcx.sess.struct_span_err_with_code(sp, msg, code)
231+
}
232+
233+
fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
234+
sp: S,
235+
msg: &str)
236+
-> DiagnosticBuilder<'a>
237+
{
238+
self.tcx.sess.struct_span_err(sp, msg)
239+
}
240+
}
241+
221242
///////////////////////////////////////////////////////////////////////////
222243
// Loans and loan paths
223244

@@ -549,14 +570,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
549570
move_data::Declared => {
550571
// If this is an uninitialized variable, just emit a simple warning
551572
// and return.
552-
struct_span_err!(
553-
self.tcx.sess, use_span, E0381,
554-
"{} of possibly uninitialized variable: `{}`",
555-
verb,
556-
self.loan_path_to_string(lp))
557-
.span_label(use_span, format!("use of possibly uninitialized `{}`",
558-
self.loan_path_to_string(lp)))
559-
.emit();
573+
self.cannot_act_on_uninitialized_variable(use_span,
574+
verb,
575+
&self.loan_path_to_string(lp),
576+
Origin::Ast)
577+
.span_label(use_span, format!("use of possibly uninitialized `{}`",
578+
self.loan_path_to_string(lp)))
579+
.emit();
560580
return;
561581
}
562582
_ => {
@@ -683,10 +703,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
683703
lp: &LoanPath<'tcx>,
684704
assign:
685705
&move_data::Assignment) {
686-
let mut err = struct_span_err!(
687-
self.tcx.sess, span, E0384,
688-
"re-assignment of immutable variable `{}`",
689-
self.loan_path_to_string(lp));
706+
let mut err = self.cannot_reassign_immutable(span,
707+
&self.loan_path_to_string(lp),
708+
Origin::Ast);
690709
err.span_label(span, "re-assignment of immutable variable");
691710
if span != assign.span {
692711
err.span_label(assign.span, format!("first assignment to `{}`",

‎src/librustc_borrowck/diagnostics.rs

Lines changed: 0 additions & 550 deletions
Large diffs are not rendered by default.

‎src/librustc_data_structures/indexed_set.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,68 @@ impl<T: Idx> IdxSet<T> {
153153
pub fn subtract(&mut self, other: &IdxSet<T>) -> bool {
154154
bitwise(self.words_mut(), other.words(), &Subtract)
155155
}
156+
157+
/// Calls `f` on each index value held in this set, up to the
158+
/// bound `max_bits` on the size of universe of indexes.
159+
pub fn each_bit<F>(&self, max_bits: usize, f: F) where F: FnMut(T) {
160+
each_bit(self, max_bits, f)
161+
}
162+
163+
/// Removes all elements from this set.
164+
pub fn reset_to_empty(&mut self) {
165+
for word in self.words_mut() { *word = 0; }
166+
}
167+
168+
pub fn elems(&self, universe_size: usize) -> Elems<T> {
169+
Elems { i: 0, set: self, universe_size: universe_size }
170+
}
171+
}
172+
173+
pub struct Elems<'a, T: Idx> { i: usize, set: &'a IdxSet<T>, universe_size: usize }
174+
175+
impl<'a, T: Idx> Iterator for Elems<'a, T> {
176+
type Item = T;
177+
fn next(&mut self) -> Option<T> {
178+
if self.i >= self.universe_size { return None; }
179+
let mut i = self.i;
180+
loop {
181+
if i >= self.universe_size {
182+
self.i = i; // (mark iteration as complete.)
183+
return None;
184+
}
185+
if self.set.contains(&T::new(i)) {
186+
self.i = i + 1; // (next element to start at.)
187+
return Some(T::new(i));
188+
}
189+
i = i + 1;
190+
}
191+
}
192+
}
193+
194+
fn each_bit<T: Idx, F>(words: &IdxSet<T>, max_bits: usize, mut f: F) where F: FnMut(T) {
195+
let usize_bits: usize = mem::size_of::<usize>() * 8;
196+
197+
for (word_index, &word) in words.words().iter().enumerate() {
198+
if word != 0 {
199+
let base_index = word_index * usize_bits;
200+
for offset in 0..usize_bits {
201+
let bit = 1 << offset;
202+
if (word & bit) != 0 {
203+
// NB: we round up the total number of bits
204+
// that we store in any given bit set so that
205+
// it is an even multiple of usize::BITS. This
206+
// means that there may be some stray bits at
207+
// the end that do not correspond to any
208+
// actual value; that's why we first check
209+
// that we are in range of bits_per_block.
210+
let bit_index = base_index + offset as usize;
211+
if bit_index >= max_bits {
212+
return;
213+
} else {
214+
f(Idx::new(bit_index));
215+
}
216+
}
217+
}
218+
}
219+
}
156220
}

‎src/librustc_driver/driver.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,14 +970,23 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
970970
// We compute "constant qualifications" between MIR_CONST and MIR_VALIDATED.
971971

972972
// What we need to run borrowck etc.
973+
973974
passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
975+
976+
// FIXME: ariel points SimplifyBranches should run after
977+
// mir-borrowck; otherwise code within `if false { ... }` would
978+
// not be checked.
974979
passes.push_pass(MIR_VALIDATED,
975980
mir::transform::simplify_branches::SimplifyBranches::new("initial"));
976981
passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts"));
977982
passes.push_pass(MIR_VALIDATED, mir::transform::nll::NLL);
978983

979984
// borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED.
980985

986+
// FIXME: niko says this should be a query (see rustc::ty::maps)
987+
// instead of a pass.
988+
passes.push_pass(MIR_VALIDATED, mir::transform::borrow_check::BorrowckMir);
989+
981990
// These next passes must be executed together
982991
passes.push_pass(MIR_OPTIMIZED, mir::transform::no_landing_pads::NoLandingPads);
983992
passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::CriticalCallEdges);

‎src/librustc_mir/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rustc = { path = "../librustc" }
1515
rustc_const_eval = { path = "../librustc_const_eval" }
1616
rustc_const_math = { path = "../librustc_const_math" }
1717
rustc_data_structures = { path = "../librustc_data_structures" }
18+
rustc_errors = { path = "../librustc_errors" }
1819
rustc_bitflags = { path = "../librustc_bitflags" }
1920
syntax = { path = "../libsyntax" }
2021
syntax_pos = { path = "../libsyntax_pos" }

‎src/librustc_mir/dataflow/drop_flag_effects.rs

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,84 +8,16 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use syntax::ast::{self, MetaItem};
1211
use syntax_pos::DUMMY_SP;
1312

14-
15-
use rustc::mir::{self, Mir, BasicBlock, Location};
16-
use rustc::session::Session;
13+
use rustc::mir::{self, Mir, Location};
1714
use rustc::ty::{self, TyCtxt};
1815
use util::elaborate_drops::DropFlagState;
19-
use rustc_data_structures::indexed_set::{IdxSet};
20-
21-
use std::fmt;
2216

23-
use super::{Dataflow, DataflowBuilder, DataflowAnalysis};
24-
use super::{BitDenotation, DataflowOperator, DataflowResults};
17+
use super::{MoveDataParamEnv};
2518
use super::indexes::MovePathIndex;
2619
use super::move_paths::{MoveData, LookupResult};
2720

28-
pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<MetaItem> {
29-
for attr in attrs {
30-
if attr.check_name("rustc_mir") {
31-
let items = attr.meta_item_list();
32-
for item in items.iter().flat_map(|l| l.iter()) {
33-
match item.meta_item() {
34-
Some(mi) if mi.check_name(name) => return Some(mi.clone()),
35-
_ => continue
36-
}
37-
}
38-
}
39-
}
40-
return None;
41-
}
42-
43-
pub struct MoveDataParamEnv<'tcx> {
44-
pub(crate) move_data: MoveData<'tcx>,
45-
pub(crate) param_env: ty::ParamEnv<'tcx>,
46-
}
47-
48-
pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
49-
mir: &Mir<'tcx>,
50-
node_id: ast::NodeId,
51-
attributes: &[ast::Attribute],
52-
dead_unwinds: &IdxSet<BasicBlock>,
53-
bd: BD,
54-
p: P)
55-
-> DataflowResults<BD>
56-
where BD: BitDenotation<Idx=MovePathIndex> + DataflowOperator,
57-
P: Fn(&BD, BD::Idx) -> &fmt::Debug
58-
{
59-
let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
60-
if let Some(item) = has_rustc_mir_with(attrs, name) {
61-
if let Some(s) = item.value_str() {
62-
return Some(s.to_string())
63-
} else {
64-
sess.span_err(
65-
item.span,
66-
&format!("{} attribute requires a path", item.name()));
67-
return None;
68-
}
69-
}
70-
return None;
71-
};
72-
73-
let print_preflow_to =
74-
name_found(tcx.sess, attributes, "borrowck_graphviz_preflow");
75-
let print_postflow_to =
76-
name_found(tcx.sess, attributes, "borrowck_graphviz_postflow");
77-
78-
let mut mbcx = DataflowBuilder {
79-
node_id,
80-
print_preflow_to,
81-
print_postflow_to,
82-
flow_state: DataflowAnalysis::new(tcx, mir, dead_unwinds, bd),
83-
};
84-
85-
mbcx.dataflow(p);
86-
mbcx.flow_state.results()
87-
}
88-
8921
pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
9022
path: MovePathIndex,
9123
mut cond: F)

‎src/librustc_mir/dataflow/graphviz.rs

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
use syntax::ast::NodeId;
1414
use rustc::mir::{BasicBlock, Mir};
1515
use rustc_data_structures::bitslice::bits_to_string;
16-
use rustc_data_structures::indexed_set::{IdxSet};
1716
use rustc_data_structures::indexed_vec::Idx;
1817

1918
use dot;
@@ -24,62 +23,13 @@ use std::fs::File;
2423
use std::io;
2524
use std::io::prelude::*;
2625
use std::marker::PhantomData;
27-
use std::mem;
2826
use std::path::Path;
2927

3028
use util;
3129

3230
use super::{BitDenotation, DataflowState};
3331
use super::DataflowBuilder;
3432

35-
impl<O: BitDenotation> DataflowState<O> {
36-
fn each_bit<F>(&self, words: &IdxSet<O::Idx>, mut f: F)
37-
where F: FnMut(O::Idx) {
38-
//! Helper for iterating over the bits in a bitvector.
39-
40-
let bits_per_block = self.operator.bits_per_block();
41-
let usize_bits: usize = mem::size_of::<usize>() * 8;
42-
43-
for (word_index, &word) in words.words().iter().enumerate() {
44-
if word != 0 {
45-
let base_index = word_index * usize_bits;
46-
for offset in 0..usize_bits {
47-
let bit = 1 << offset;
48-
if (word & bit) != 0 {
49-
// NB: we round up the total number of bits
50-
// that we store in any given bit set so that
51-
// it is an even multiple of usize::BITS. This
52-
// means that there may be some stray bits at
53-
// the end that do not correspond to any
54-
// actual value; that's why we first check
55-
// that we are in range of bits_per_block.
56-
let bit_index = base_index + offset as usize;
57-
if bit_index >= bits_per_block {
58-
return;
59-
} else {
60-
f(O::Idx::new(bit_index));
61-
}
62-
}
63-
}
64-
}
65-
}
66-
}
67-
68-
pub fn interpret_set<'c, P>(&self,
69-
o: &'c O,
70-
words: &IdxSet<O::Idx>,
71-
render_idx: &P)
72-
-> Vec<&'c Debug>
73-
where P: Fn(&O, O::Idx) -> &Debug
74-
{
75-
let mut v = Vec::new();
76-
self.each_bit(words, |i| {
77-
v.push(render_idx(o, i));
78-
});
79-
v
80-
}
81-
}
82-
8333
pub trait MirWithFlowState<'tcx> {
8434
type BD: BitDenotation;
8535
fn node_id(&self) -> NodeId;
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::mir::{self, Location, Mir};
12+
use rustc::mir::visit::Visitor;
13+
use rustc::ty::{Region, TyCtxt};
14+
use rustc::ty::RegionKind::ReScope;
15+
use rustc::util::nodemap::{FxHashMap, FxHashSet};
16+
17+
use rustc_data_structures::bitslice::{BitwiseOperator};
18+
use rustc_data_structures::indexed_set::{IdxSet};
19+
use rustc_data_structures::indexed_vec::{IndexVec};
20+
21+
use dataflow::{BitDenotation, BlockSets, DataflowOperator};
22+
pub use dataflow::indexes::BorrowIndex;
23+
24+
use std::fmt;
25+
26+
// `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be
27+
// uniquely identified in the MIR by the `Location` of the assigment
28+
// statement in which it appears on the right hand side.
29+
pub struct Borrows<'a, 'tcx: 'a> {
30+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
31+
mir: &'a Mir<'tcx>,
32+
borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
33+
location_map: FxHashMap<Location, BorrowIndex>,
34+
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
35+
}
36+
37+
// temporarily allow some dead fields: `kind` and `region` will be
38+
// needed by borrowck; `lvalue` will probably be a MovePathIndex when
39+
// that is extended to include borrowed data paths.
40+
#[allow(dead_code)]
41+
#[derive(Debug)]
42+
pub struct BorrowData<'tcx> {
43+
pub(crate) location: Location,
44+
pub(crate) kind: mir::BorrowKind,
45+
pub(crate) region: Region<'tcx>,
46+
pub(crate) lvalue: mir::Lvalue<'tcx>,
47+
}
48+
49+
impl<'tcx> fmt::Display for BorrowData<'tcx> {
50+
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
51+
let kind = match self.kind {
52+
mir::BorrowKind::Shared => "",
53+
mir::BorrowKind::Unique => "uniq ",
54+
mir::BorrowKind::Mut => "mut ",
55+
};
56+
let region = format!("{}", self.region);
57+
let region = if region.len() > 0 { format!("{} ", region) } else { region };
58+
write!(w, "&{}{}{:?}", region, kind, self.lvalue)
59+
}
60+
}
61+
62+
impl<'a, 'tcx> Borrows<'a, 'tcx> {
63+
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
64+
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
65+
location_map: FxHashMap(),
66+
region_map: FxHashMap(), };
67+
visitor.visit_mir(mir);
68+
return Borrows { tcx: tcx,
69+
mir: mir,
70+
borrows: visitor.idx_vec,
71+
location_map: visitor.location_map,
72+
region_map: visitor.region_map, };
73+
74+
struct GatherBorrows<'tcx> {
75+
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
76+
location_map: FxHashMap<Location, BorrowIndex>,
77+
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
78+
}
79+
impl<'tcx> Visitor<'tcx> for GatherBorrows<'tcx> {
80+
fn visit_rvalue(&mut self,
81+
rvalue: &mir::Rvalue<'tcx>,
82+
location: mir::Location) {
83+
if let mir::Rvalue::Ref(region, kind, ref lvalue) = *rvalue {
84+
let borrow = BorrowData {
85+
location: location, kind: kind, region: region, lvalue: lvalue.clone(),
86+
};
87+
let idx = self.idx_vec.push(borrow);
88+
self.location_map.insert(location, idx);
89+
let borrows = self.region_map.entry(region).or_insert(FxHashSet());
90+
borrows.insert(idx);
91+
}
92+
}
93+
}
94+
}
95+
96+
pub fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrows }
97+
98+
pub fn location(&self, idx: BorrowIndex) -> &Location {
99+
&self.borrows[idx].location
100+
}
101+
}
102+
103+
impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
104+
type Idx = BorrowIndex;
105+
fn name() -> &'static str { "borrows" }
106+
fn bits_per_block(&self) -> usize {
107+
self.borrows.len()
108+
}
109+
fn start_block_effect(&self, _sets: &mut BlockSets<BorrowIndex>) {
110+
// no borrows of code extents have been taken prior to
111+
// function execution, so this method has no effect on
112+
// `_sets`.
113+
}
114+
fn statement_effect(&self,
115+
sets: &mut BlockSets<BorrowIndex>,
116+
location: Location) {
117+
let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
118+
panic!("could not find block at location {:?}", location);
119+
});
120+
let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
121+
panic!("could not find statement at location {:?}");
122+
});
123+
match stmt.kind {
124+
mir::StatementKind::EndRegion(extent) => {
125+
let borrow_indexes = self.region_map.get(&ReScope(extent)).unwrap_or_else(|| {
126+
panic!("could not find BorrowIndexs for code-extent {:?}", extent);
127+
});
128+
129+
for idx in borrow_indexes { sets.kill(&idx); }
130+
}
131+
132+
mir::StatementKind::Assign(_, ref rhs) => {
133+
if let mir::Rvalue::Ref(region, _, _) = *rhs {
134+
let index = self.location_map.get(&location).unwrap_or_else(|| {
135+
panic!("could not find BorrowIndex for location {:?}", location);
136+
});
137+
assert!(self.region_map.get(region).unwrap_or_else(|| {
138+
panic!("could not find BorrowIndexs for region {:?}", region);
139+
}).contains(&index));
140+
sets.gen(&index);
141+
}
142+
}
143+
144+
mir::StatementKind::InlineAsm { .. } |
145+
mir::StatementKind::SetDiscriminant { .. } |
146+
mir::StatementKind::StorageLive(..) |
147+
mir::StatementKind::StorageDead(..) |
148+
mir::StatementKind::Validate(..) |
149+
mir::StatementKind::Nop => {}
150+
151+
}
152+
}
153+
fn terminator_effect(&self,
154+
_sets: &mut BlockSets<BorrowIndex>,
155+
_location: Location) {
156+
// no terminators start nor end code extents.
157+
}
158+
159+
fn propagate_call_return(&self,
160+
_in_out: &mut IdxSet<BorrowIndex>,
161+
_call_bb: mir::BasicBlock,
162+
_dest_bb: mir::BasicBlock,
163+
_dest_lval: &mir::Lvalue) {
164+
// there are no effects on the extents from method calls.
165+
}
166+
}
167+
168+
impl<'a, 'tcx> BitwiseOperator for Borrows<'a, 'tcx> {
169+
#[inline]
170+
fn join(&self, pred1: usize, pred2: usize) -> usize {
171+
pred1 | pred2 // union effects of preds when computing borrows
172+
}
173+
}
174+
175+
impl<'a, 'tcx> DataflowOperator for Borrows<'a, 'tcx> {
176+
#[inline]
177+
fn bottom_value() -> bool {
178+
false // bottom = no Rvalue::Refs are active by default
179+
}
180+
}

‎src/librustc_mir/dataflow/impls/mod.rs

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ use super::drop_flag_effects_for_function_entry;
3030
use super::drop_flag_effects_for_location;
3131
use super::on_lookup_result_bits;
3232

33+
#[allow(dead_code)]
34+
pub(super) mod borrows;
35+
3336
/// `MaybeInitializedLvals` tracks all l-values that might be
3437
/// initialized upon reaching a particular point in the control flow
3538
/// for a function.
@@ -287,24 +290,22 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
287290

288291
fn statement_effect(&self,
289292
sets: &mut BlockSets<MovePathIndex>,
290-
bb: mir::BasicBlock,
291-
idx: usize)
293+
location: Location)
292294
{
293295
drop_flag_effects_for_location(
294296
self.tcx, self.mir, self.mdpe,
295-
Location { block: bb, statement_index: idx },
297+
location,
296298
|path, s| Self::update_bits(sets, path, s)
297299
)
298300
}
299301

300302
fn terminator_effect(&self,
301303
sets: &mut BlockSets<MovePathIndex>,
302-
bb: mir::BasicBlock,
303-
statements_len: usize)
304+
location: Location)
304305
{
305306
drop_flag_effects_for_location(
306307
self.tcx, self.mir, self.mdpe,
307-
Location { block: bb, statement_index: statements_len },
308+
location,
308309
|path, s| Self::update_bits(sets, path, s)
309310
)
310311
}
@@ -344,24 +345,22 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
344345

345346
fn statement_effect(&self,
346347
sets: &mut BlockSets<MovePathIndex>,
347-
bb: mir::BasicBlock,
348-
idx: usize)
348+
location: Location)
349349
{
350350
drop_flag_effects_for_location(
351351
self.tcx, self.mir, self.mdpe,
352-
Location { block: bb, statement_index: idx },
352+
location,
353353
|path, s| Self::update_bits(sets, path, s)
354354
)
355355
}
356356

357357
fn terminator_effect(&self,
358358
sets: &mut BlockSets<MovePathIndex>,
359-
bb: mir::BasicBlock,
360-
statements_len: usize)
359+
location: Location)
361360
{
362361
drop_flag_effects_for_location(
363362
self.tcx, self.mir, self.mdpe,
364-
Location { block: bb, statement_index: statements_len },
363+
location,
365364
|path, s| Self::update_bits(sets, path, s)
366365
)
367366
}
@@ -400,24 +399,22 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
400399

401400
fn statement_effect(&self,
402401
sets: &mut BlockSets<MovePathIndex>,
403-
bb: mir::BasicBlock,
404-
idx: usize)
402+
location: Location)
405403
{
406404
drop_flag_effects_for_location(
407405
self.tcx, self.mir, self.mdpe,
408-
Location { block: bb, statement_index: idx },
406+
location,
409407
|path, s| Self::update_bits(sets, path, s)
410408
)
411409
}
412410

413411
fn terminator_effect(&self,
414412
sets: &mut BlockSets<MovePathIndex>,
415-
bb: mir::BasicBlock,
416-
statements_len: usize)
413+
location: Location)
417414
{
418415
drop_flag_effects_for_location(
419416
self.tcx, self.mir, self.mdpe,
420-
Location { block: bb, statement_index: statements_len },
417+
location,
421418
|path, s| Self::update_bits(sets, path, s)
422419
)
423420
}
@@ -448,18 +445,16 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
448445
}
449446
fn statement_effect(&self,
450447
sets: &mut BlockSets<MoveOutIndex>,
451-
bb: mir::BasicBlock,
452-
idx: usize) {
448+
location: Location) {
453449
let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
454-
let stmt = &mir[bb].statements[idx];
450+
let stmt = &mir[location.block].statements[location.statement_index];
455451
let loc_map = &move_data.loc_map;
456452
let path_map = &move_data.path_map;
457453
let rev_lookup = &move_data.rev_lookup;
458454

459-
let loc = Location { block: bb, statement_index: idx };
460455
debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
461-
stmt, loc, &loc_map[loc]);
462-
for move_index in &loc_map[loc] {
456+
stmt, location, &loc_map[location]);
457+
for move_index in &loc_map[location] {
463458
// Every path deinitialized by a *particular move*
464459
// has corresponding bit, "gen'ed" (i.e. set)
465460
// here, in dataflow vector
@@ -506,17 +501,15 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
506501

507502
fn terminator_effect(&self,
508503
sets: &mut BlockSets<MoveOutIndex>,
509-
bb: mir::BasicBlock,
510-
statements_len: usize)
504+
location: Location)
511505
{
512506
let (mir, move_data) = (self.mir, self.move_data());
513-
let term = mir[bb].terminator();
507+
let term = mir[location.block].terminator();
514508
let loc_map = &move_data.loc_map;
515-
let loc = Location { block: bb, statement_index: statements_len };
516509
debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
517-
term, loc, &loc_map[loc]);
510+
term, location, &loc_map[location]);
518511
let bits_per_block = self.bits_per_block();
519-
for move_index in &loc_map[loc] {
512+
for move_index in &loc_map[location] {
520513
assert!(move_index.index() < bits_per_block);
521514
zero_to_one(sets.gen_set.words_mut(), *move_index);
522515
}

‎src/librustc_mir/dataflow/mod.rs

Lines changed: 237 additions & 34 deletions
Large diffs are not rendered by default.
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::ty::{self, TyCtxt};
12+
use rustc::mir::*;
13+
use rustc::mir::tcx::RvalueInitializationState;
14+
use rustc::util::nodemap::FxHashMap;
15+
use rustc_data_structures::indexed_vec::{IndexVec};
16+
17+
use syntax::codemap::DUMMY_SP;
18+
19+
use std::collections::hash_map::Entry;
20+
use std::mem;
21+
22+
use super::abs_domain::Lift;
23+
24+
use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex};
25+
26+
pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> {
27+
mir: &'a Mir<'tcx>,
28+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
29+
param_env: ty::ParamEnv<'tcx>,
30+
data: MoveData<'tcx>,
31+
}
32+
33+
pub enum MovePathError {
34+
IllegalMove,
35+
UnionMove { path: MovePathIndex },
36+
}
37+
38+
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
39+
fn new(mir: &'a Mir<'tcx>,
40+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
41+
param_env: ty::ParamEnv<'tcx>)
42+
-> Self {
43+
let mut move_paths = IndexVec::new();
44+
let mut path_map = IndexVec::new();
45+
46+
MoveDataBuilder {
47+
mir,
48+
tcx,
49+
param_env,
50+
data: MoveData {
51+
moves: IndexVec::new(),
52+
loc_map: LocationMap::new(mir),
53+
rev_lookup: MovePathLookup {
54+
locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| {
55+
Self::new_move_path(&mut move_paths, &mut path_map, None, v)
56+
}).collect(),
57+
projections: FxHashMap(),
58+
},
59+
move_paths,
60+
path_map,
61+
}
62+
}
63+
}
64+
65+
fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
66+
path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
67+
parent: Option<MovePathIndex>,
68+
lvalue: Lvalue<'tcx>)
69+
-> MovePathIndex
70+
{
71+
let move_path = move_paths.push(MovePath {
72+
next_sibling: None,
73+
first_child: None,
74+
parent,
75+
lvalue,
76+
});
77+
78+
if let Some(parent) = parent {
79+
let next_sibling =
80+
mem::replace(&mut move_paths[parent].first_child, Some(move_path));
81+
move_paths[move_path].next_sibling = next_sibling;
82+
}
83+
84+
let path_map_ent = path_map.push(vec![]);
85+
assert_eq!(path_map_ent, move_path);
86+
move_path
87+
}
88+
89+
/// This creates a MovePath for a given lvalue, returning an `MovePathError`
90+
/// if that lvalue can't be moved from.
91+
///
92+
/// NOTE: lvalues behind references *do not* get a move path, which is
93+
/// problematic for borrowck.
94+
///
95+
/// Maybe we should have separate "borrowck" and "moveck" modes.
96+
fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
97+
-> Result<MovePathIndex, MovePathError>
98+
{
99+
debug!("lookup({:?})", lval);
100+
match *lval {
101+
Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]),
102+
// error: can't move out of a static
103+
Lvalue::Static(..) => Err(MovePathError::IllegalMove),
104+
Lvalue::Projection(ref proj) => {
105+
self.move_path_for_projection(lval, proj)
106+
}
107+
}
108+
}
109+
110+
fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
111+
// This is an assignment, not a move, so this not being a valid
112+
// move path is OK.
113+
let _ = self.move_path_for(lval);
114+
}
115+
116+
fn move_path_for_projection(&mut self,
117+
lval: &Lvalue<'tcx>,
118+
proj: &LvalueProjection<'tcx>)
119+
-> Result<MovePathIndex, MovePathError>
120+
{
121+
let base = try!(self.move_path_for(&proj.base));
122+
let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
123+
match lv_ty.sty {
124+
// error: can't move out of borrowed content
125+
ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
126+
// error: can't move out of struct with destructor
127+
ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() =>
128+
return Err(MovePathError::IllegalMove),
129+
// move out of union - always move the entire union
130+
ty::TyAdt(adt, _) if adt.is_union() =>
131+
return Err(MovePathError::UnionMove { path: base }),
132+
// error: can't move out of a slice
133+
ty::TySlice(..) =>
134+
return Err(MovePathError::IllegalMove),
135+
ty::TyArray(..) => match proj.elem {
136+
// error: can't move out of an array
137+
ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
138+
_ => {
139+
// FIXME: still badly broken
140+
}
141+
},
142+
_ => {}
143+
};
144+
match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
145+
Entry::Occupied(ent) => Ok(*ent.get()),
146+
Entry::Vacant(ent) => {
147+
let path = Self::new_move_path(
148+
&mut self.data.move_paths,
149+
&mut self.data.path_map,
150+
Some(base),
151+
lval.clone()
152+
);
153+
ent.insert(path);
154+
Ok(path)
155+
}
156+
}
157+
}
158+
159+
fn finalize(self) -> MoveData<'tcx> {
160+
debug!("{}", {
161+
debug!("moves for {:?}:", self.mir.span);
162+
for (j, mo) in self.data.moves.iter_enumerated() {
163+
debug!(" {:?} = {:?}", j, mo);
164+
}
165+
debug!("move paths for {:?}:", self.mir.span);
166+
for (j, path) in self.data.move_paths.iter_enumerated() {
167+
debug!(" {:?} = {:?}", j, path);
168+
}
169+
"done dumping moves"
170+
});
171+
self.data
172+
}
173+
}
174+
175+
pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
176+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
177+
param_env: ty::ParamEnv<'tcx>)
178+
-> MoveData<'tcx> {
179+
let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
180+
181+
for (bb, block) in mir.basic_blocks().iter_enumerated() {
182+
for (i, stmt) in block.statements.iter().enumerate() {
183+
let source = Location { block: bb, statement_index: i };
184+
builder.gather_statement(source, stmt);
185+
}
186+
187+
let terminator_loc = Location {
188+
block: bb,
189+
statement_index: block.statements.len()
190+
};
191+
builder.gather_terminator(terminator_loc, block.terminator());
192+
}
193+
194+
builder.finalize()
195+
}
196+
197+
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
198+
fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
199+
debug!("gather_statement({:?}, {:?})", loc, stmt);
200+
match stmt.kind {
201+
StatementKind::Assign(ref lval, ref rval) => {
202+
self.create_move_path(lval);
203+
if let RvalueInitializationState::Shallow = rval.initialization_state() {
204+
// Box starts out uninitialized - need to create a separate
205+
// move-path for the interior so it will be separate from
206+
// the exterior.
207+
self.create_move_path(&lval.clone().deref());
208+
}
209+
self.gather_rvalue(loc, rval);
210+
}
211+
StatementKind::StorageLive(_) |
212+
StatementKind::StorageDead(_) => {}
213+
StatementKind::SetDiscriminant{ .. } => {
214+
span_bug!(stmt.source_info.span,
215+
"SetDiscriminant should not exist during borrowck");
216+
}
217+
StatementKind::InlineAsm { .. } |
218+
StatementKind::EndRegion(_) |
219+
StatementKind::Validate(..) |
220+
StatementKind::Nop => {}
221+
}
222+
}
223+
224+
fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
225+
match *rvalue {
226+
Rvalue::Use(ref operand) |
227+
Rvalue::Repeat(ref operand, _) |
228+
Rvalue::Cast(_, ref operand, _) |
229+
Rvalue::UnaryOp(_, ref operand) => {
230+
self.gather_operand(loc, operand)
231+
}
232+
Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
233+
Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
234+
self.gather_operand(loc, lhs);
235+
self.gather_operand(loc, rhs);
236+
}
237+
Rvalue::Aggregate(ref _kind, ref operands) => {
238+
for operand in operands {
239+
self.gather_operand(loc, operand);
240+
}
241+
}
242+
Rvalue::Ref(..) |
243+
Rvalue::Discriminant(..) |
244+
Rvalue::Len(..) |
245+
Rvalue::NullaryOp(NullOp::SizeOf, _) |
246+
Rvalue::NullaryOp(NullOp::Box, _) => {
247+
// This returns an rvalue with uninitialized contents. We can't
248+
// move out of it here because it is an rvalue - assignments always
249+
// completely initialize their lvalue.
250+
//
251+
// However, this does not matter - MIR building is careful to
252+
// only emit a shallow free for the partially-initialized
253+
// temporary.
254+
//
255+
// In any case, if we want to fix this, we have to register a
256+
// special move and change the `statement_effect` functions.
257+
}
258+
}
259+
}
260+
261+
fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
262+
debug!("gather_terminator({:?}, {:?})", loc, term);
263+
match term.kind {
264+
TerminatorKind::Goto { target: _ } |
265+
TerminatorKind::Resume |
266+
TerminatorKind::Unreachable => { }
267+
268+
TerminatorKind::Return => {
269+
self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
270+
}
271+
272+
TerminatorKind::Assert { .. } |
273+
TerminatorKind::SwitchInt { .. } => {
274+
// branching terminators - these don't move anything
275+
}
276+
277+
TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
278+
self.gather_move(loc, location);
279+
}
280+
TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
281+
self.create_move_path(location);
282+
self.gather_operand(loc, value);
283+
}
284+
TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
285+
self.gather_operand(loc, func);
286+
for arg in args {
287+
self.gather_operand(loc, arg);
288+
}
289+
if let Some((ref destination, _bb)) = *destination {
290+
self.create_move_path(destination);
291+
}
292+
}
293+
}
294+
}
295+
296+
fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) {
297+
match *operand {
298+
Operand::Constant(..) => {} // not-a-move
299+
Operand::Consume(ref lval) => { // a move
300+
self.gather_move(loc, lval);
301+
}
302+
}
303+
}
304+
305+
fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) {
306+
debug!("gather_move({:?}, {:?})", loc, lval);
307+
308+
let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx);
309+
if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) {
310+
debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty);
311+
return
312+
}
313+
314+
let path = match self.move_path_for(lval) {
315+
Ok(path) | Err(MovePathError::UnionMove { path }) => path,
316+
Err(MovePathError::IllegalMove) => {
317+
// Moving out of a bad path. Eventually, this should be a MIR
318+
// borrowck error instead of a bug.
319+
span_bug!(self.mir.span,
320+
"Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
321+
lval, lv_ty, loc);
322+
}
323+
};
324+
let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
325+
326+
debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
327+
loc, lval, move_out, path);
328+
329+
self.data.path_map[path].push(move_out);
330+
self.data.loc_map[loc].push(move_out);
331+
}
332+
}

‎src/librustc_mir/dataflow/move_paths/mod.rs

Lines changed: 11 additions & 313 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,10 @@
1111

1212
use rustc::ty::{self, TyCtxt};
1313
use rustc::mir::*;
14-
use rustc::mir::tcx::RvalueInitializationState;
1514
use rustc::util::nodemap::FxHashMap;
1615
use rustc_data_structures::indexed_vec::{IndexVec};
1716

18-
use syntax::codemap::DUMMY_SP;
19-
20-
use std::collections::hash_map::Entry;
2117
use std::fmt;
22-
use std::mem;
2318
use std::ops::{Index, IndexMut};
2419

2520
use self::abs_domain::{AbstractElem, Lift};
@@ -63,6 +58,9 @@ pub(crate) mod indexes {
6358

6459
/// Index into MoveData.moves.
6560
new_index!(MoveOutIndex, "mo");
61+
62+
/// Index into Borrows.locations
63+
new_index!(BorrowIndex, "bw");
6664
}
6765

6866
pub use self::indexes::MovePathIndex;
@@ -110,6 +108,12 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> {
110108
}
111109
}
112110

111+
impl<'tcx> fmt::Display for MovePath<'tcx> {
112+
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
113+
write!(w, "{:?}", self.lvalue)
114+
}
115+
}
116+
113117
#[derive(Debug)]
114118
pub struct MoveData<'tcx> {
115119
pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
@@ -191,154 +195,7 @@ pub struct MovePathLookup<'tcx> {
191195
projections: FxHashMap<(MovePathIndex, AbstractElem<'tcx>), MovePathIndex>
192196
}
193197

194-
pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> {
195-
mir: &'a Mir<'tcx>,
196-
tcx: TyCtxt<'a, 'tcx, 'tcx>,
197-
param_env: ty::ParamEnv<'tcx>,
198-
data: MoveData<'tcx>,
199-
}
200-
201-
pub enum MovePathError {
202-
IllegalMove,
203-
UnionMove { path: MovePathIndex },
204-
}
205-
206-
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
207-
fn new(mir: &'a Mir<'tcx>,
208-
tcx: TyCtxt<'a, 'tcx, 'tcx>,
209-
param_env: ty::ParamEnv<'tcx>)
210-
-> Self {
211-
let mut move_paths = IndexVec::new();
212-
let mut path_map = IndexVec::new();
213-
214-
MoveDataBuilder {
215-
mir,
216-
tcx,
217-
param_env,
218-
data: MoveData {
219-
moves: IndexVec::new(),
220-
loc_map: LocationMap::new(mir),
221-
rev_lookup: MovePathLookup {
222-
locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| {
223-
Self::new_move_path(&mut move_paths, &mut path_map, None, v)
224-
}).collect(),
225-
projections: FxHashMap(),
226-
},
227-
move_paths,
228-
path_map,
229-
}
230-
}
231-
}
232-
233-
fn new_move_path(move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>,
234-
path_map: &mut IndexVec<MovePathIndex, Vec<MoveOutIndex>>,
235-
parent: Option<MovePathIndex>,
236-
lvalue: Lvalue<'tcx>)
237-
-> MovePathIndex
238-
{
239-
let move_path = move_paths.push(MovePath {
240-
next_sibling: None,
241-
first_child: None,
242-
parent,
243-
lvalue,
244-
});
245-
246-
if let Some(parent) = parent {
247-
let next_sibling =
248-
mem::replace(&mut move_paths[parent].first_child, Some(move_path));
249-
move_paths[move_path].next_sibling = next_sibling;
250-
}
251-
252-
let path_map_ent = path_map.push(vec![]);
253-
assert_eq!(path_map_ent, move_path);
254-
move_path
255-
}
256-
257-
/// This creates a MovePath for a given lvalue, returning an `MovePathError`
258-
/// if that lvalue can't be moved from.
259-
///
260-
/// NOTE: lvalues behind references *do not* get a move path, which is
261-
/// problematic for borrowck.
262-
///
263-
/// Maybe we should have separate "borrowck" and "moveck" modes.
264-
fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
265-
-> Result<MovePathIndex, MovePathError>
266-
{
267-
debug!("lookup({:?})", lval);
268-
match *lval {
269-
Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]),
270-
// error: can't move out of a static
271-
Lvalue::Static(..) => Err(MovePathError::IllegalMove),
272-
Lvalue::Projection(ref proj) => {
273-
self.move_path_for_projection(lval, proj)
274-
}
275-
}
276-
}
277-
278-
fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
279-
// This is an assignment, not a move, so this not being a valid
280-
// move path is OK.
281-
let _ = self.move_path_for(lval);
282-
}
283-
284-
fn move_path_for_projection(&mut self,
285-
lval: &Lvalue<'tcx>,
286-
proj: &LvalueProjection<'tcx>)
287-
-> Result<MovePathIndex, MovePathError>
288-
{
289-
let base = try!(self.move_path_for(&proj.base));
290-
let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
291-
match lv_ty.sty {
292-
// error: can't move out of borrowed content
293-
ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
294-
// error: can't move out of struct with destructor
295-
ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() =>
296-
return Err(MovePathError::IllegalMove),
297-
// move out of union - always move the entire union
298-
ty::TyAdt(adt, _) if adt.is_union() =>
299-
return Err(MovePathError::UnionMove { path: base }),
300-
// error: can't move out of a slice
301-
ty::TySlice(..) =>
302-
return Err(MovePathError::IllegalMove),
303-
ty::TyArray(..) => match proj.elem {
304-
// error: can't move out of an array
305-
ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
306-
_ => {
307-
// FIXME: still badly broken
308-
}
309-
},
310-
_ => {}
311-
};
312-
match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) {
313-
Entry::Occupied(ent) => Ok(*ent.get()),
314-
Entry::Vacant(ent) => {
315-
let path = Self::new_move_path(
316-
&mut self.data.move_paths,
317-
&mut self.data.path_map,
318-
Some(base),
319-
lval.clone()
320-
);
321-
ent.insert(path);
322-
Ok(path)
323-
}
324-
}
325-
}
326-
327-
fn finalize(self) -> MoveData<'tcx> {
328-
debug!("{}", {
329-
debug!("moves for {:?}:", self.mir.span);
330-
for (j, mo) in self.data.moves.iter_enumerated() {
331-
debug!(" {:?} = {:?}", j, mo);
332-
}
333-
debug!("move paths for {:?}:", self.mir.span);
334-
for (j, path) in self.data.move_paths.iter_enumerated() {
335-
debug!(" {:?} = {:?}", j, path);
336-
}
337-
"done dumping moves"
338-
});
339-
self.data
340-
}
341-
}
198+
mod builder;
342199

343200
#[derive(Copy, Clone, Debug)]
344201
pub enum LookupResult {
@@ -375,165 +232,6 @@ impl<'a, 'tcx> MoveData<'tcx> {
375232
tcx: TyCtxt<'a, 'tcx, 'tcx>,
376233
param_env: ty::ParamEnv<'tcx>)
377234
-> Self {
378-
gather_moves(mir, tcx, param_env)
379-
}
380-
}
381-
382-
fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
383-
tcx: TyCtxt<'a, 'tcx, 'tcx>,
384-
param_env: ty::ParamEnv<'tcx>)
385-
-> MoveData<'tcx> {
386-
let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
387-
388-
for (bb, block) in mir.basic_blocks().iter_enumerated() {
389-
for (i, stmt) in block.statements.iter().enumerate() {
390-
let source = Location { block: bb, statement_index: i };
391-
builder.gather_statement(source, stmt);
392-
}
393-
394-
let terminator_loc = Location {
395-
block: bb,
396-
statement_index: block.statements.len()
397-
};
398-
builder.gather_terminator(terminator_loc, block.terminator());
399-
}
400-
401-
builder.finalize()
402-
}
403-
404-
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
405-
fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
406-
debug!("gather_statement({:?}, {:?})", loc, stmt);
407-
match stmt.kind {
408-
StatementKind::Assign(ref lval, ref rval) => {
409-
self.create_move_path(lval);
410-
if let RvalueInitializationState::Shallow = rval.initialization_state() {
411-
// Box starts out uninitialized - need to create a separate
412-
// move-path for the interior so it will be separate from
413-
// the exterior.
414-
self.create_move_path(&lval.clone().deref());
415-
}
416-
self.gather_rvalue(loc, rval);
417-
}
418-
StatementKind::StorageLive(_) |
419-
StatementKind::StorageDead(_) => {}
420-
StatementKind::SetDiscriminant{ .. } => {
421-
span_bug!(stmt.source_info.span,
422-
"SetDiscriminant should not exist during borrowck");
423-
}
424-
StatementKind::InlineAsm { .. } |
425-
StatementKind::EndRegion(_) |
426-
StatementKind::Validate(..) |
427-
StatementKind::Nop => {}
428-
}
429-
}
430-
431-
fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) {
432-
match *rvalue {
433-
Rvalue::Use(ref operand) |
434-
Rvalue::Repeat(ref operand, _) |
435-
Rvalue::Cast(_, ref operand, _) |
436-
Rvalue::UnaryOp(_, ref operand) => {
437-
self.gather_operand(loc, operand)
438-
}
439-
Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) |
440-
Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => {
441-
self.gather_operand(loc, lhs);
442-
self.gather_operand(loc, rhs);
443-
}
444-
Rvalue::Aggregate(ref _kind, ref operands) => {
445-
for operand in operands {
446-
self.gather_operand(loc, operand);
447-
}
448-
}
449-
Rvalue::Ref(..) |
450-
Rvalue::Discriminant(..) |
451-
Rvalue::Len(..) |
452-
Rvalue::NullaryOp(NullOp::SizeOf, _) |
453-
Rvalue::NullaryOp(NullOp::Box, _) => {
454-
// This returns an rvalue with uninitialized contents. We can't
455-
// move out of it here because it is an rvalue - assignments always
456-
// completely initialize their lvalue.
457-
//
458-
// However, this does not matter - MIR building is careful to
459-
// only emit a shallow free for the partially-initialized
460-
// temporary.
461-
//
462-
// In any case, if we want to fix this, we have to register a
463-
// special move and change the `statement_effect` functions.
464-
}
465-
}
466-
}
467-
468-
fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
469-
debug!("gather_terminator({:?}, {:?})", loc, term);
470-
match term.kind {
471-
TerminatorKind::Goto { target: _ } |
472-
TerminatorKind::Resume |
473-
TerminatorKind::Unreachable => { }
474-
475-
TerminatorKind::Return => {
476-
self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
477-
}
478-
479-
TerminatorKind::Assert { .. } |
480-
TerminatorKind::SwitchInt { .. } => {
481-
// branching terminators - these don't move anything
482-
}
483-
484-
TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
485-
self.gather_move(loc, location);
486-
}
487-
TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
488-
self.create_move_path(location);
489-
self.gather_operand(loc, value);
490-
}
491-
TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
492-
self.gather_operand(loc, func);
493-
for arg in args {
494-
self.gather_operand(loc, arg);
495-
}
496-
if let Some((ref destination, _bb)) = *destination {
497-
self.create_move_path(destination);
498-
}
499-
}
500-
}
501-
}
502-
503-
fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) {
504-
match *operand {
505-
Operand::Constant(..) => {} // not-a-move
506-
Operand::Consume(ref lval) => { // a move
507-
self.gather_move(loc, lval);
508-
}
509-
}
510-
}
511-
512-
fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) {
513-
debug!("gather_move({:?}, {:?})", loc, lval);
514-
515-
let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx);
516-
if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) {
517-
debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty);
518-
return
519-
}
520-
521-
let path = match self.move_path_for(lval) {
522-
Ok(path) | Err(MovePathError::UnionMove { path }) => path,
523-
Err(MovePathError::IllegalMove) => {
524-
// Moving out of a bad path. Eventually, this should be a MIR
525-
// borrowck error instead of a bug.
526-
span_bug!(self.mir.span,
527-
"Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
528-
lval, lv_ty, loc);
529-
}
530-
};
531-
let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
532-
533-
debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
534-
loc, lval, move_out, path);
535-
536-
self.data.path_map[path].push(move_out);
537-
self.data.loc_map[loc].push(move_out);
235+
builder::gather_moves(mir, tcx, param_env)
538236
}
539237
}

‎src/librustc_mir/diagnostics.rs

Lines changed: 551 additions & 0 deletions
Large diffs are not rendered by default.

‎src/librustc_mir/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ extern crate graphviz as dot;
3232
#[macro_use]
3333
extern crate rustc;
3434
extern crate rustc_data_structures;
35+
extern crate rustc_errors;
3536
#[macro_use]
3637
#[no_link]
3738
extern crate rustc_bitflags;

‎src/librustc_mir/transform/borrow_check.rs

Lines changed: 1277 additions & 0 deletions
Large diffs are not rendered by default.

‎src/librustc_mir/transform/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub mod simplify;
3131
pub mod erase_regions;
3232
pub mod no_landing_pads;
3333
pub mod type_check;
34+
pub mod borrow_check;
3435
pub mod rustc_peek;
3536
pub mod elaborate_drops;
3637
pub mod add_call_guards;

‎src/librustc_mir/transform/rustc_peek.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use syntax::ast;
1313
use syntax_pos::Span;
1414

1515
use rustc::ty::{self, TyCtxt};
16-
use rustc::mir::{self, Mir};
16+
use rustc::mir::{self, Mir, Location};
1717
use rustc::mir::transform::{MirPass, MirSource};
1818
use rustc_data_structures::indexed_set::IdxSetBuf;
1919
use rustc_data_structures::indexed_vec::Idx;
@@ -202,7 +202,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
202202
// reset GEN and KILL sets before emulating their effect.
203203
for e in sets.gen_set.words_mut() { *e = 0; }
204204
for e in sets.kill_set.words_mut() { *e = 0; }
205-
results.0.operator.statement_effect(&mut sets, bb, j);
205+
results.0.operator.statement_effect(&mut sets, Location { block: bb, statement_index: j });
206206
sets.on_entry.union(sets.gen_set);
207207
sets.on_entry.subtract(sets.kill_set);
208208
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::ty::{self, TyCtxt};
12+
use rustc_errors::DiagnosticBuilder;
13+
use syntax_pos::{MultiSpan, Span};
14+
15+
use std::fmt;
16+
17+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
18+
pub enum Origin { Ast, Mir }
19+
20+
impl fmt::Display for Origin {
21+
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
22+
match *self {
23+
Origin::Mir => write!(w, " (Mir)"),
24+
Origin::Ast => ty::tls::with_opt(|opt_tcx| {
25+
// If user passed `-Z borrowck-mir`, then include an
26+
// AST origin as part of the error report
27+
if let Some(tcx) = opt_tcx {
28+
if tcx.sess.opts.debugging_opts.borrowck_mir {
29+
return write!(w, " (Ast)");
30+
}
31+
}
32+
// otherwise, do not include the origin (i.e., print
33+
// nothing at all)
34+
Ok(())
35+
}),
36+
}
37+
}
38+
}
39+
40+
pub trait BorrowckErrors {
41+
fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
42+
sp: S,
43+
msg: &str,
44+
code: &str)
45+
-> DiagnosticBuilder<'a>;
46+
47+
fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
48+
sp: S,
49+
msg: &str)
50+
-> DiagnosticBuilder<'a>;
51+
52+
fn cannot_move_when_borrowed(&self, span: Span, desc: &str, o: Origin)
53+
-> DiagnosticBuilder
54+
{
55+
struct_span_err!(self, span, E0505,
56+
"cannot move out of `{}` because it is borrowed{OGN}",
57+
desc, OGN=o)
58+
}
59+
60+
fn cannot_use_when_mutably_borrowed(&self, span: Span, desc: &str, o: Origin)
61+
-> DiagnosticBuilder
62+
{
63+
struct_span_err!(self, span, E0503,
64+
"cannot use `{}` because it was mutably borrowed{OGN}",
65+
desc, OGN=o)
66+
}
67+
68+
fn cannot_act_on_uninitialized_variable(&self,
69+
span: Span,
70+
verb: &str,
71+
desc: &str,
72+
o: Origin)
73+
-> DiagnosticBuilder
74+
{
75+
struct_span_err!(self, span, E0381,
76+
"{} of possibly uninitialized variable: `{}`{OGN}",
77+
verb, desc, OGN=o)
78+
}
79+
80+
fn cannot_mutably_borrow_multiply(&self,
81+
span: Span,
82+
desc: &str,
83+
opt_via: &str,
84+
o: Origin)
85+
-> DiagnosticBuilder
86+
{
87+
struct_span_err!(self, span, E0499,
88+
"cannot borrow `{}`{} as mutable more than once at a time{OGN}",
89+
desc, opt_via, OGN=o)
90+
}
91+
92+
fn cannot_uniquely_borrow_by_two_closures(&self, span: Span, desc: &str, o: Origin)
93+
-> DiagnosticBuilder
94+
{
95+
struct_span_err!(self, span, E0524,
96+
"two closures require unique access to `{}` at the same time{OGN}",
97+
desc, OGN=o)
98+
}
99+
100+
fn cannot_uniquely_borrow_by_one_closure(&self,
101+
span: Span,
102+
desc_new: &str,
103+
noun_old: &str,
104+
msg_old: &str,
105+
o: Origin)
106+
-> DiagnosticBuilder
107+
{
108+
struct_span_err!(self, span, E0500,
109+
"closure requires unique access to `{}` but {} is already borrowed{}{OGN}",
110+
desc_new, noun_old, msg_old, OGN=o)
111+
}
112+
113+
fn cannot_reborrow_already_uniquely_borrowed(&self,
114+
span: Span,
115+
desc_new: &str,
116+
msg_new: &str,
117+
kind_new: &str,
118+
o: Origin)
119+
-> DiagnosticBuilder
120+
{
121+
struct_span_err!(self, span, E0501,
122+
"cannot borrow `{}`{} as {} because previous closure \
123+
requires unique access{OGN}",
124+
desc_new, msg_new, kind_new, OGN=o)
125+
}
126+
127+
fn cannot_reborrow_already_borrowed(&self,
128+
span: Span,
129+
desc_new: &str,
130+
msg_new: &str,
131+
kind_new: &str,
132+
noun_old: &str,
133+
kind_old: &str,
134+
msg_old: &str,
135+
o: Origin)
136+
-> DiagnosticBuilder
137+
{
138+
struct_span_err!(self, span, E0502,
139+
"cannot borrow `{}`{} as {} because {} is also borrowed as {}{}{OGN}",
140+
desc_new, msg_new, kind_new, noun_old, kind_old, msg_old, OGN=o)
141+
}
142+
143+
fn cannot_assign_to_borrowed(&self, span: Span, desc: &str, o: Origin)
144+
-> DiagnosticBuilder
145+
{
146+
struct_span_err!(self, span, E0506,
147+
"cannot assign to `{}` because it is borrowed{OGN}",
148+
desc, OGN=o)
149+
}
150+
151+
fn cannot_move_into_closure(&self, span: Span, desc: &str, o: Origin)
152+
-> DiagnosticBuilder
153+
{
154+
struct_span_err!(self, span, E0504,
155+
"cannot move `{}` into closure because it is borrowed{OGN}",
156+
desc, OGN=o)
157+
}
158+
159+
fn cannot_reassign_immutable(&self, span: Span, desc: &str, o: Origin)
160+
-> DiagnosticBuilder
161+
{
162+
struct_span_err!(self, span, E0384,
163+
"re-assignment of immutable variable `{}`{OGN}",
164+
desc, OGN=o)
165+
}
166+
167+
fn cannot_assign_static(&self, span: Span, desc: &str, o: Origin)
168+
-> DiagnosticBuilder
169+
{
170+
self.struct_span_err(span, &format!("cannot assign to immutable static item {}{OGN}",
171+
desc, OGN=o))
172+
}
173+
}
174+
175+
impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> {
176+
fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
177+
sp: S,
178+
msg: &str,
179+
code: &str)
180+
-> DiagnosticBuilder<'a>
181+
{
182+
self.sess.struct_span_err_with_code(sp, msg, code)
183+
}
184+
185+
fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
186+
sp: S,
187+
msg: &str)
188+
-> DiagnosticBuilder<'a>
189+
{
190+
self.sess.struct_span_err(sp, msg)
191+
}
192+
}

‎src/librustc_mir/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
pub mod borrowck_errors;
1112
pub mod elaborate_drops;
1213
pub mod def_use;
1314
pub mod patch;

0 commit comments

Comments
 (0)
Please sign in to comment.