|
1 | 1 | //! MIR borrow checker, which is used in diagnostics like `unused_mut` |
2 | 2 |
|
3 | 3 | // Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these |
4 | | -// and implement a proper borrow checker. |
| 4 | +// if needed for implementing a proper borrow checker. |
5 | 5 |
|
| 6 | +use std::sync::Arc; |
| 7 | + |
| 8 | +use hir_def::DefWithBodyId; |
6 | 9 | use la_arena::ArenaMap; |
7 | 10 | use stdx::never; |
8 | 11 |
|
| 12 | +use crate::db::HirDatabase; |
| 13 | + |
9 | 14 | use super::{ |
10 | | - BasicBlockId, BorrowKind, LocalId, MirBody, MirSpan, Place, ProjectionElem, Rvalue, |
11 | | - StatementKind, Terminator, |
| 15 | + BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem, |
| 16 | + Rvalue, StatementKind, Terminator, |
12 | 17 | }; |
13 | 18 |
|
14 | | -#[derive(Debug)] |
15 | | -pub enum Mutability { |
16 | | - Mut { span: MirSpan }, |
| 19 | +#[derive(Debug, Clone, PartialEq, Eq)] |
| 20 | +/// Stores spans which implies that the local should be mutable. |
| 21 | +pub enum MutabilityReason { |
| 22 | + Mut { spans: Vec<MirSpan> }, |
17 | 23 | Not, |
18 | 24 | } |
19 | 25 |
|
| 26 | +#[derive(Debug, Clone, PartialEq, Eq)] |
| 27 | +pub struct BorrowckResult { |
| 28 | + pub mir_body: Arc<MirBody>, |
| 29 | + pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>, |
| 30 | +} |
| 31 | + |
| 32 | +pub fn borrowck_query( |
| 33 | + db: &dyn HirDatabase, |
| 34 | + def: DefWithBodyId, |
| 35 | +) -> Result<Arc<BorrowckResult>, MirLowerError> { |
| 36 | + let body = db.mir_body(def)?; |
| 37 | + let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body }; |
| 38 | + Ok(Arc::new(r)) |
| 39 | +} |
| 40 | + |
20 | 41 | fn is_place_direct(lvalue: &Place) -> bool { |
21 | 42 | !lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref) |
22 | 43 | } |
@@ -116,49 +137,49 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local |
116 | 137 | } |
117 | 138 | } |
118 | 139 | } |
119 | | - for (b, block) in body.basic_blocks.iter() { |
120 | | - for statement in &block.statements { |
121 | | - if let StatementKind::Assign(p, _) = &statement.kind { |
122 | | - if p.projection.len() == 0 { |
123 | | - let l = p.local; |
124 | | - if !result[b].contains_idx(l) { |
125 | | - result[b].insert(l, false); |
126 | | - dfs(body, b, l, &mut result); |
127 | | - } |
128 | | - } |
129 | | - } |
| 140 | + for &l in &body.param_locals { |
| 141 | + result[body.start_block].insert(l, true); |
| 142 | + dfs(body, body.start_block, l, &mut result); |
| 143 | + } |
| 144 | + for l in body.locals.iter().map(|x| x.0) { |
| 145 | + if !result[body.start_block].contains_idx(l) { |
| 146 | + result[body.start_block].insert(l, false); |
| 147 | + dfs(body, body.start_block, l, &mut result); |
130 | 148 | } |
131 | 149 | } |
132 | 150 | result |
133 | 151 | } |
134 | 152 |
|
135 | | -pub fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, Mutability> { |
136 | | - let mut result: ArenaMap<LocalId, Mutability> = |
137 | | - body.locals.iter().map(|x| (x.0, Mutability::Not)).collect(); |
| 153 | +fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> { |
| 154 | + let mut result: ArenaMap<LocalId, MutabilityReason> = |
| 155 | + body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect(); |
| 156 | + let mut push_mut_span = |local, span| match &mut result[local] { |
| 157 | + MutabilityReason::Mut { spans } => spans.push(span), |
| 158 | + x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] }, |
| 159 | + }; |
138 | 160 | let ever_init_maps = ever_initialized_map(body); |
139 | | - for (block_id, ever_init_map) in ever_init_maps.iter() { |
140 | | - let mut ever_init_map = ever_init_map.clone(); |
| 161 | + for (block_id, mut ever_init_map) in ever_init_maps.into_iter() { |
141 | 162 | let block = &body.basic_blocks[block_id]; |
142 | 163 | for statement in &block.statements { |
143 | 164 | match &statement.kind { |
144 | 165 | StatementKind::Assign(place, value) => { |
145 | 166 | match place_case(place) { |
146 | 167 | ProjectionCase::Direct => { |
147 | 168 | if ever_init_map.get(place.local).copied().unwrap_or_default() { |
148 | | - result[place.local] = Mutability::Mut { span: statement.span }; |
| 169 | + push_mut_span(place.local, statement.span); |
149 | 170 | } else { |
150 | 171 | ever_init_map.insert(place.local, true); |
151 | 172 | } |
152 | 173 | } |
153 | 174 | ProjectionCase::DirectPart => { |
154 | 175 | // Partial initialization is not supported, so it is definitely `mut` |
155 | | - result[place.local] = Mutability::Mut { span: statement.span }; |
| 176 | + push_mut_span(place.local, statement.span); |
156 | 177 | } |
157 | 178 | ProjectionCase::Indirect => (), |
158 | 179 | } |
159 | 180 | if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value { |
160 | 181 | if is_place_direct(p) { |
161 | | - result[p.local] = Mutability::Mut { span: statement.span }; |
| 182 | + push_mut_span(p.local, statement.span); |
162 | 183 | } |
163 | 184 | } |
164 | 185 | } |
@@ -189,7 +210,7 @@ pub fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, Mutability> { |
189 | 210 | Terminator::Call { destination, .. } => { |
190 | 211 | if destination.projection.len() == 0 { |
191 | 212 | if ever_init_map.get(destination.local).copied().unwrap_or_default() { |
192 | | - result[destination.local] = Mutability::Mut { span: MirSpan::Unknown }; |
| 213 | + push_mut_span(destination.local, MirSpan::Unknown); |
193 | 214 | } else { |
194 | 215 | ever_init_map.insert(destination.local, true); |
195 | 216 | } |
|
0 commit comments