|
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