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.rs | 202 |
1 files changed, 119 insertions, 83 deletions
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index fb0c0dee09..db16c94396 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -12,11 +12,14 @@ use stdx::never; use triomphe::Arc; use crate::{ - ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags, - db::{HirDatabase, InternedClosure}, + TraitEnvironment, + db::{HirDatabase, InternedClosure, InternedClosureId}, display::DisplayTarget, mir::OperandKind, - utils::ClosureSubst, + next_solver::{ + DbInterner, GenericArgs, SolverDefIds, Ty, TypingMode, + infer::{DbInternerInferExt, InferCtxt}, + }, }; use super::{ @@ -33,45 +36,45 @@ pub enum MutabilityReason { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct MovedOutOfRef { - pub ty: Ty, +pub struct MovedOutOfRef<'db> { + pub ty: Ty<'db>, pub span: MirSpan, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct PartiallyMoved { - pub ty: Ty, +pub struct PartiallyMoved<'db> { + pub ty: Ty<'db>, pub span: MirSpan, - pub local: LocalId, + pub local: LocalId<'db>, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct BorrowRegion { - pub local: LocalId, +pub struct BorrowRegion<'db> { + pub local: LocalId<'db>, pub kind: BorrowKind, pub places: Vec<MirSpan>, } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct BorrowckResult { - pub mir_body: Arc<MirBody>, - pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>, - pub moved_out_of_ref: Vec<MovedOutOfRef>, - pub partially_moved: Vec<PartiallyMoved>, - pub borrow_regions: Vec<BorrowRegion>, +pub struct BorrowckResult<'db> { + pub mir_body: Arc<MirBody<'db>>, + pub mutability_of_locals: ArenaMap<LocalId<'db>, MutabilityReason>, + pub moved_out_of_ref: Vec<MovedOutOfRef<'db>>, + pub partially_moved: Vec<PartiallyMoved<'db>>, + pub borrow_regions: Vec<BorrowRegion<'db>>, } -fn all_mir_bodies( - db: &dyn HirDatabase, +fn all_mir_bodies<'db>( + db: &'db dyn HirDatabase, def: DefWithBodyId, - mut cb: impl FnMut(Arc<MirBody>), -) -> Result<(), MirLowerError> { - fn for_closure( - db: &dyn HirDatabase, - c: ClosureId, - cb: &mut impl FnMut(Arc<MirBody>), - ) -> Result<(), MirLowerError> { - match db.mir_body_for_closure(c.into()) { + mut cb: impl FnMut(Arc<MirBody<'db>>), +) -> Result<(), MirLowerError<'db>> { + fn for_closure<'db>( + db: &'db dyn HirDatabase, + c: InternedClosureId, + cb: &mut impl FnMut(Arc<MirBody<'db>>), + ) -> Result<(), MirLowerError<'db>> { + match db.mir_body_for_closure(c) { Ok(body) => { cb(body.clone()); body.closures.iter().try_for_each(|&it| for_closure(db, it, cb)) @@ -88,17 +91,24 @@ fn all_mir_bodies( } } -pub fn borrowck_query( - db: &dyn HirDatabase, +pub fn borrowck_query<'db>( + db: &'db dyn HirDatabase, def: DefWithBodyId, -) -> Result<Arc<[BorrowckResult]>, MirLowerError> { +) -> Result<Arc<[BorrowckResult<'db>]>, MirLowerError<'db>> { let _p = tracing::info_span!("borrowck_query").entered(); + let module = def.module(db); + let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block()); + let env = db.trait_environment_for_body(def); let mut res = vec![]; all_mir_bodies(db, def, |body| { + // FIXME(next-solver): Opaques. + let infcx = interner.infer_ctxt().build(TypingMode::Borrowck { + defining_opaque_types: SolverDefIds::new_from_iter(interner, []), + }); res.push(BorrowckResult { - mutability_of_locals: mutability_of_locals(db, &body), - moved_out_of_ref: moved_out_of_ref(db, &body), - partially_moved: partially_moved(db, &body), + mutability_of_locals: mutability_of_locals(&infcx, &body), + moved_out_of_ref: moved_out_of_ref(&infcx, &env, &body), + partially_moved: partially_moved(&infcx, &env, &body), borrow_regions: borrow_regions(db, &body), mir_body: body, }); @@ -106,43 +116,49 @@ pub fn borrowck_query( Ok(res.into()) } -fn make_fetch_closure_field( - db: &dyn HirDatabase, -) -> impl FnOnce(ClosureId, &Substitution, usize) -> Ty + '_ { - |c: ClosureId, subst: &Substitution, f: usize| { - let InternedClosure(def, _) = db.lookup_intern_closure(c.into()); +fn make_fetch_closure_field<'db>( + db: &'db dyn HirDatabase, +) -> impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db> + use<'db> { + |c: InternedClosureId, subst: GenericArgs<'db>, f: usize| { + let InternedClosure(def, _) = db.lookup_intern_closure(c); let infer = db.infer(def); - let (captures, _) = infer.closure_info(&c); - let parent_subst = ClosureSubst(subst).parent_subst(); - captures.get(f).expect("broken closure field").ty.clone().substitute(Interner, parent_subst) + let (captures, _) = infer.closure_info(c); + let parent_subst = subst.split_closure_args_untupled().parent_args; + let interner = DbInterner::new_with(db, None, None); + captures.get(f).expect("broken closure field").ty.instantiate(interner, parent_subst) } } -fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> { +fn moved_out_of_ref<'db>( + infcx: &InferCtxt<'db>, + env: &TraitEnvironment<'db>, + body: &MirBody<'db>, +) -> Vec<MovedOutOfRef<'db>> { + let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand<'db>, span: MirSpan| match op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - let mut ty: Ty = body.locals[p.local].ty.clone(); + let mut ty: Ty<'db> = body.locals[p.local].ty; let mut is_dereference_of_ref = false; for proj in p.projection.lookup(&body.projection_store) { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } ty = proj.projected_ty( + infcx, ty, - db, make_fetch_closure_field(db), body.owner.module(db).krate(), ); } if is_dereference_of_ref - && !ty.clone().is_copy(db, body.owner) - && !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR) + && !infcx.type_is_copy_modulo_regions(env.env, ty) + && !ty.references_non_lt_error() { result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty }); } } - OperandKind::Constant(_) | OperandKind::Static(_) => (), + OperandKind::Constant { .. } | OperandKind::Static(_) => (), }; for (_, block) in body.basic_blocks.iter() { db.unwind_if_revision_cancelled(); @@ -213,26 +229,29 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> result } -fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved> { +fn partially_moved<'db>( + infcx: &InferCtxt<'db>, + env: &TraitEnvironment<'db>, + body: &MirBody<'db>, +) -> Vec<PartiallyMoved<'db>> { + let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand<'db>, span: MirSpan| match op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - let mut ty: Ty = body.locals[p.local].ty.clone(); + let mut ty: Ty<'db> = body.locals[p.local].ty; for proj in p.projection.lookup(&body.projection_store) { ty = proj.projected_ty( + infcx, ty, - db, make_fetch_closure_field(db), body.owner.module(db).krate(), ); } - if !ty.clone().is_copy(db, body.owner) - && !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR) - { + if !infcx.type_is_copy_modulo_regions(env.env, ty) && !ty.references_non_lt_error() { result.push(PartiallyMoved { span, ty, local: p.local }); } } - OperandKind::Constant(_) | OperandKind::Static(_) => (), + OperandKind::Constant { .. } | OperandKind::Static(_) => (), }; for (_, block) in body.basic_blocks.iter() { db.unwind_if_revision_cancelled(); @@ -303,7 +322,7 @@ fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved> result } -fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec<BorrowRegion> { +fn borrow_regions<'db>(db: &'db dyn HirDatabase, body: &MirBody<'db>) -> Vec<BorrowRegion<'db>> { let mut borrows = FxHashMap::default(); for (_, block) in body.basic_blocks.iter() { db.unwind_if_revision_cancelled(); @@ -311,7 +330,7 @@ fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec<BorrowRegion> { if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind { borrows .entry(p.local) - .and_modify(|it: &mut BorrowRegion| { + .and_modify(|it: &mut BorrowRegion<'db>| { it.places.push(statement.span); }) .or_insert_with(|| BorrowRegion { @@ -353,9 +372,14 @@ enum ProjectionCase { Indirect, } -fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase { +fn place_case<'db>( + infcx: &InferCtxt<'db>, + body: &MirBody<'db>, + lvalue: &Place<'db>, +) -> ProjectionCase { + let db = infcx.interner.db; let mut is_part_of = false; - let mut ty = body.locals[lvalue.local].ty.clone(); + let mut ty = body.locals[lvalue.local].ty; for proj in lvalue.projection.lookup(&body.projection_store).iter() { match proj { ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw @@ -369,7 +393,12 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio } ProjectionElem::OpaqueCast(_) => (), } - ty = proj.projected_ty(ty, db, make_fetch_closure_field(db), body.owner.module(db).krate()); + ty = proj.projected_ty( + infcx, + ty, + make_fetch_closure_field(db), + body.owner.module(db).krate(), + ); } if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct } } @@ -377,18 +406,18 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio /// Returns a map from basic blocks to the set of locals that might be ever initialized before /// the start of the block. Only `StorageDead` can remove something from this map, and we ignore /// `Uninit` and `drop` and similar after initialization. -fn ever_initialized_map( - db: &dyn HirDatabase, - body: &MirBody, -) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> { - let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> = +fn ever_initialized_map<'db>( + db: &'db dyn HirDatabase, + body: &MirBody<'db>, +) -> ArenaMap<BasicBlockId<'db>, ArenaMap<LocalId<'db>, bool>> { + let mut result: ArenaMap<BasicBlockId<'db>, ArenaMap<LocalId<'db>, bool>> = body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect(); - fn dfs( - db: &dyn HirDatabase, - body: &MirBody, - l: LocalId, - stack: &mut Vec<BasicBlockId>, - result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>, + fn dfs<'db>( + db: &'db dyn HirDatabase, + body: &MirBody<'db>, + l: LocalId<'db>, + stack: &mut Vec<BasicBlockId<'db>>, + result: &mut ArenaMap<BasicBlockId<'db>, ArenaMap<LocalId<'db>, bool>>, ) { while let Some(b) = stack.pop() { let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs @@ -476,7 +505,11 @@ fn ever_initialized_map( result } -fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) { +fn push_mut_span<'db>( + local: LocalId<'db>, + span: MirSpan, + result: &mut ArenaMap<LocalId<'db>, MutabilityReason>, +) { match &mut result[local] { MutabilityReason::Mut { spans } => spans.push(span), it @ (MutabilityReason::Not | MutabilityReason::Unused) => { @@ -485,23 +518,27 @@ fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, M }; } -fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) { +fn record_usage<'db>(local: LocalId<'db>, result: &mut ArenaMap<LocalId<'db>, MutabilityReason>) { if let it @ MutabilityReason::Unused = &mut result[local] { *it = MutabilityReason::Not; }; } -fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) { +fn record_usage_for_operand<'db>( + arg: &Operand<'db>, + result: &mut ArenaMap<LocalId<'db>, MutabilityReason>, +) { if let OperandKind::Copy(p) | OperandKind::Move(p) = arg.kind { record_usage(p.local, result); } } -fn mutability_of_locals( - db: &dyn HirDatabase, - body: &MirBody, -) -> ArenaMap<LocalId, MutabilityReason> { - let mut result: ArenaMap<LocalId, MutabilityReason> = +fn mutability_of_locals<'db>( + infcx: &InferCtxt<'db>, + body: &MirBody<'db>, +) -> ArenaMap<LocalId<'db>, MutabilityReason> { + let db = infcx.interner.db; + let mut result: ArenaMap<LocalId<'db>, MutabilityReason> = body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect(); let ever_init_maps = ever_initialized_map(db, body); @@ -510,7 +547,7 @@ fn mutability_of_locals( for statement in &block.statements { match &statement.kind { StatementKind::Assign(place, value) => { - match place_case(db, body, place) { + match place_case(infcx, body, place) { ProjectionCase::Direct => { if ever_init_map.get(place.local).copied().unwrap_or_default() { push_mut_span(place.local, statement.span, &mut result); @@ -559,10 +596,9 @@ fn mutability_of_locals( }, p, ) = value + && place_case(infcx, body, p) != ProjectionCase::Indirect { - if place_case(db, body, p) != ProjectionCase::Indirect { - push_mut_span(p.local, statement.span, &mut result); - } + push_mut_span(p.local, statement.span, &mut result); } } StatementKind::FakeRead(p) => { |