Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/borrowck.rs')
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs73
1 files changed, 47 insertions, 26 deletions
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index fcf9a67fe8..7bd0f888b3 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -1,22 +1,43 @@
//! MIR borrow checker, which is used in diagnostics like `unused_mut`
// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
-// and implement a proper borrow checker.
+// if needed for implementing a proper borrow checker.
+use std::sync::Arc;
+
+use hir_def::DefWithBodyId;
use la_arena::ArenaMap;
use stdx::never;
+use crate::db::HirDatabase;
+
use super::{
- BasicBlockId, BorrowKind, LocalId, MirBody, MirSpan, Place, ProjectionElem, Rvalue,
- StatementKind, Terminator,
+ BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
+ Rvalue, StatementKind, Terminator,
};
-#[derive(Debug)]
-pub enum Mutability {
- Mut { span: MirSpan },
+#[derive(Debug, Clone, PartialEq, Eq)]
+/// Stores spans which implies that the local should be mutable.
+pub enum MutabilityReason {
+ Mut { spans: Vec<MirSpan> },
Not,
}
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct BorrowckResult {
+ pub mir_body: Arc<MirBody>,
+ pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
+}
+
+pub fn borrowck_query(
+ db: &dyn HirDatabase,
+ def: DefWithBodyId,
+) -> Result<Arc<BorrowckResult>, MirLowerError> {
+ let body = db.mir_body(def)?;
+ let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body };
+ Ok(Arc::new(r))
+}
+
fn is_place_direct(lvalue: &Place) -> bool {
!lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref)
}
@@ -116,28 +137,28 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
}
}
}
- for (b, block) in body.basic_blocks.iter() {
- for statement in &block.statements {
- if let StatementKind::Assign(p, _) = &statement.kind {
- if p.projection.len() == 0 {
- let l = p.local;
- if !result[b].contains_idx(l) {
- result[b].insert(l, false);
- dfs(body, b, l, &mut result);
- }
- }
- }
+ for &l in &body.param_locals {
+ result[body.start_block].insert(l, true);
+ dfs(body, body.start_block, l, &mut result);
+ }
+ for l in body.locals.iter().map(|x| x.0) {
+ if !result[body.start_block].contains_idx(l) {
+ result[body.start_block].insert(l, false);
+ dfs(body, body.start_block, l, &mut result);
}
}
result
}
-pub fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, Mutability> {
- let mut result: ArenaMap<LocalId, Mutability> =
- body.locals.iter().map(|x| (x.0, Mutability::Not)).collect();
+fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
+ let mut result: ArenaMap<LocalId, MutabilityReason> =
+ body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect();
+ let mut push_mut_span = |local, span| match &mut result[local] {
+ MutabilityReason::Mut { spans } => spans.push(span),
+ x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] },
+ };
let ever_init_maps = ever_initialized_map(body);
- for (block_id, ever_init_map) in ever_init_maps.iter() {
- let mut ever_init_map = ever_init_map.clone();
+ for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
let block = &body.basic_blocks[block_id];
for statement in &block.statements {
match &statement.kind {
@@ -145,20 +166,20 @@ pub fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, Mutability> {
match place_case(place) {
ProjectionCase::Direct => {
if ever_init_map.get(place.local).copied().unwrap_or_default() {
- result[place.local] = Mutability::Mut { span: statement.span };
+ push_mut_span(place.local, statement.span);
} else {
ever_init_map.insert(place.local, true);
}
}
ProjectionCase::DirectPart => {
// Partial initialization is not supported, so it is definitely `mut`
- result[place.local] = Mutability::Mut { span: statement.span };
+ push_mut_span(place.local, statement.span);
}
ProjectionCase::Indirect => (),
}
if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value {
if is_place_direct(p) {
- result[p.local] = Mutability::Mut { span: statement.span };
+ push_mut_span(p.local, statement.span);
}
}
}
@@ -189,7 +210,7 @@ pub fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, Mutability> {
Terminator::Call { destination, .. } => {
if destination.projection.len() == 0 {
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
- result[destination.local] = Mutability::Mut { span: MirSpan::Unknown };
+ push_mut_span(destination.local, MirSpan::Unknown);
} else {
ever_init_map.insert(destination.local, true);
}