|
11 | 11 | use borrow_check::borrow_set::BorrowData;
|
12 | 12 | use borrow_check::nll::region_infer::Cause;
|
13 | 13 | use borrow_check::{Context, MirBorrowckCtxt, WriteKind};
|
14 |
| -use rustc::mir::Place; |
| 14 | +use rustc::mir::{Location, Place, TerminatorKind}; |
15 | 15 | use rustc_errors::DiagnosticBuilder;
|
16 | 16 |
|
17 | 17 | mod find_use;
|
@@ -63,10 +63,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
63 | 63 |
|
64 | 64 | match find_use::find(mir, regioncx, tcx, region_sub, context.loc) {
|
65 | 65 | Some(Cause::LiveVar(_local, location)) => {
|
66 |
| - err.span_label( |
67 |
| - mir.source_info(location).span, |
68 |
| - "borrow later used here".to_string(), |
69 |
| - ); |
| 66 | + if self.is_borrow_location_in_loop(context.loc) { |
| 67 | + err.span_label( |
| 68 | + mir.source_info(location).span, |
| 69 | + "borrow used here in later iteration of loop".to_string(), |
| 70 | + ); |
| 71 | + } else { |
| 72 | + err.span_label( |
| 73 | + mir.source_info(location).span, |
| 74 | + "borrow later used here".to_string(), |
| 75 | + ); |
| 76 | + } |
70 | 77 | }
|
71 | 78 |
|
72 | 79 | Some(Cause::DropVar(local, location)) => match &mir.local_decls[local].name {
|
@@ -107,4 +114,76 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
107 | 114 | }
|
108 | 115 | }
|
109 | 116 | }
|
| 117 | + |
| 118 | + /// Check if a borrow location is within a loop. |
| 119 | + fn is_borrow_location_in_loop( |
| 120 | + &self, |
| 121 | + borrow_location: Location, |
| 122 | + ) -> bool { |
| 123 | + let mut visited_locations = Vec::new(); |
| 124 | + let mut pending_locations = vec![ borrow_location ]; |
| 125 | + debug!("is_in_loop: borrow_location={:?}", borrow_location); |
| 126 | + |
| 127 | + while let Some(location) = pending_locations.pop() { |
| 128 | + debug!("is_in_loop: location={:?} pending_locations={:?} visited_locations={:?}", |
| 129 | + location, pending_locations, visited_locations); |
| 130 | + if location == borrow_location && visited_locations.contains(&borrow_location) { |
| 131 | + // We've managed to return to where we started (and this isn't the start of the |
| 132 | + // search). |
| 133 | + debug!("is_in_loop: found!"); |
| 134 | + return true; |
| 135 | + } |
| 136 | + |
| 137 | + // Skip locations we've been. |
| 138 | + if visited_locations.contains(&location) { continue; } |
| 139 | + |
| 140 | + let block = &self.mir.basic_blocks()[location.block]; |
| 141 | + if location.statement_index == block.statements.len() { |
| 142 | + // Add start location of the next blocks to pending locations. |
| 143 | + match block.terminator().kind { |
| 144 | + TerminatorKind::Goto { target } => { |
| 145 | + pending_locations.push(target.start_location()); |
| 146 | + }, |
| 147 | + TerminatorKind::SwitchInt { ref targets, .. } => { |
| 148 | + for target in targets { |
| 149 | + pending_locations.push(target.start_location()); |
| 150 | + } |
| 151 | + }, |
| 152 | + TerminatorKind::Drop { target, unwind, .. } | |
| 153 | + TerminatorKind::DropAndReplace { target, unwind, .. } | |
| 154 | + TerminatorKind::Assert { target, cleanup: unwind, .. } | |
| 155 | + TerminatorKind::Yield { resume: target, drop: unwind, .. } | |
| 156 | + TerminatorKind::FalseUnwind { real_target: target, unwind, .. } => { |
| 157 | + pending_locations.push(target.start_location()); |
| 158 | + if let Some(unwind) = unwind { |
| 159 | + pending_locations.push(unwind.start_location()); |
| 160 | + } |
| 161 | + }, |
| 162 | + TerminatorKind::Call { ref destination, cleanup, .. } => { |
| 163 | + if let Some((_, destination)) = destination { |
| 164 | + pending_locations.push(destination.start_location()); |
| 165 | + } |
| 166 | + if let Some(cleanup) = cleanup { |
| 167 | + pending_locations.push(cleanup.start_location()); |
| 168 | + } |
| 169 | + }, |
| 170 | + TerminatorKind::FalseEdges { real_target, ref imaginary_targets, .. } => { |
| 171 | + pending_locations.push(real_target.start_location()); |
| 172 | + for target in imaginary_targets { |
| 173 | + pending_locations.push(target.start_location()); |
| 174 | + } |
| 175 | + }, |
| 176 | + _ => {}, |
| 177 | + } |
| 178 | + } else { |
| 179 | + // Add the next statement to pending locations. |
| 180 | + pending_locations.push(location.successor_within_block()); |
| 181 | + } |
| 182 | + |
| 183 | + // Keep track of where we have visited. |
| 184 | + visited_locations.push(location); |
| 185 | + } |
| 186 | + |
| 187 | + false |
| 188 | + } |
110 | 189 | }
|
0 commit comments