mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 04:57:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			179 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| #![deny(rustc::untranslatable_diagnostic)]
 | |
| #![deny(rustc::diagnostic_outside_of_impl)]
 | |
| use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
 | |
| use crate::places_conflict;
 | |
| use crate::AccessDepth;
 | |
| use crate::BorrowIndex;
 | |
| use crate::Upvar;
 | |
| use rustc_data_structures::graph::dominators::Dominators;
 | |
| use rustc_middle::mir::BorrowKind;
 | |
| use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem};
 | |
| use rustc_middle::ty::TyCtxt;
 | |
| use rustc_target::abi::FieldIdx;
 | |
| 
 | |
| /// Returns `true` if the borrow represented by `kind` is
 | |
| /// allowed to be split into separate Reservation and
 | |
| /// Activation phases.
 | |
| pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool {
 | |
|     kind.allows_two_phase_borrow()
 | |
| }
 | |
| 
 | |
| /// Control for the path borrow checking code
 | |
| #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 | |
| pub(super) enum Control {
 | |
|     Continue,
 | |
|     Break,
 | |
| }
 | |
| 
 | |
| /// Encapsulates the idea of iterating over every borrow that involves a particular path
 | |
| pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
 | |
|     s: &mut S,
 | |
|     tcx: TyCtxt<'tcx>,
 | |
|     body: &Body<'tcx>,
 | |
|     _location: Location,
 | |
|     access_place: (AccessDepth, Place<'tcx>),
 | |
|     borrow_set: &BorrowSet<'tcx>,
 | |
|     is_candidate: I,
 | |
|     mut op: F,
 | |
| ) where
 | |
|     F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control,
 | |
|     I: Fn(BorrowIndex) -> bool,
 | |
| {
 | |
|     let (access, place) = access_place;
 | |
| 
 | |
|     // The number of candidates can be large, but borrows for different locals cannot conflict with
 | |
|     // each other, so we restrict the working set a priori.
 | |
|     let Some(borrows_for_place_base) = borrow_set.local_map.get(&place.local) else { return };
 | |
| 
 | |
|     // check for loan restricting path P being used. Accounts for
 | |
|     // borrows of P, P.a.b, etc.
 | |
|     for &i in borrows_for_place_base {
 | |
|         if !is_candidate(i) {
 | |
|             continue;
 | |
|         }
 | |
|         let borrowed = &borrow_set[i];
 | |
| 
 | |
|         if places_conflict::borrow_conflicts_with_place(
 | |
|             tcx,
 | |
|             body,
 | |
|             borrowed.borrowed_place,
 | |
|             borrowed.kind,
 | |
|             place.as_ref(),
 | |
|             access,
 | |
|             places_conflict::PlaceConflictBias::Overlap,
 | |
|         ) {
 | |
|             debug!(
 | |
|                 "each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
 | |
|                 i, borrowed, place, access
 | |
|             );
 | |
|             let ctrl = op(s, i, borrowed);
 | |
|             if ctrl == Control::Break {
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(super) fn is_active<'tcx>(
 | |
|     dominators: &Dominators<BasicBlock>,
 | |
|     borrow_data: &BorrowData<'tcx>,
 | |
|     location: Location,
 | |
| ) -> bool {
 | |
|     debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
 | |
| 
 | |
|     let activation_location = match borrow_data.activation_location {
 | |
|         // If this is not a 2-phase borrow, it is always active.
 | |
|         TwoPhaseActivation::NotTwoPhase => return true,
 | |
|         // And if the unique 2-phase use is not an activation, then it is *never* active.
 | |
|         TwoPhaseActivation::NotActivated => return false,
 | |
|         // Otherwise, we derive info from the activation point `loc`:
 | |
|         TwoPhaseActivation::ActivatedAt(loc) => loc,
 | |
|     };
 | |
| 
 | |
|     // Otherwise, it is active for every location *except* in between
 | |
|     // the reservation and the activation:
 | |
|     //
 | |
|     //       X
 | |
|     //      /
 | |
|     //     R      <--+ Except for this
 | |
|     //    / \        | diamond
 | |
|     //    \ /        |
 | |
|     //     A  <------+
 | |
|     //     |
 | |
|     //     Z
 | |
|     //
 | |
|     // Note that we assume that:
 | |
|     // - the reservation R dominates the activation A
 | |
|     // - the activation A post-dominates the reservation R (ignoring unwinding edges).
 | |
|     //
 | |
|     // This means that there can't be an edge that leaves A and
 | |
|     // comes back into that diamond unless it passes through R.
 | |
|     //
 | |
|     // Suboptimal: In some cases, this code walks the dominator
 | |
|     // tree twice when it only has to be walked once. I am
 | |
|     // lazy. -nmatsakis
 | |
| 
 | |
|     // If dominated by the activation A, then it is active. The
 | |
|     // activation occurs upon entering the point A, so this is
 | |
|     // also true if location == activation_location.
 | |
|     if activation_location.dominates(location, dominators) {
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     // The reservation starts *on exiting* the reservation block,
 | |
|     // so check if the location is dominated by R.successor. If so,
 | |
|     // this point falls in between the reservation and location.
 | |
|     let reserve_location = borrow_data.reserve_location.successor_within_block();
 | |
|     if reserve_location.dominates(location, dominators) {
 | |
|         false
 | |
|     } else {
 | |
|         // Otherwise, this point is outside the diamond, so
 | |
|         // consider the borrow active. This could happen for
 | |
|         // example if the borrow remains active around a loop (in
 | |
|         // which case it would be active also for the point R,
 | |
|         // which would generate an error).
 | |
|         true
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Determines if a given borrow is borrowing local data
 | |
| /// This is called for all Yield expressions on movable generators
 | |
| pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
 | |
|     // Reborrow of already borrowed data is ignored
 | |
|     // Any errors will be caught on the initial borrow
 | |
|     !place.is_indirect()
 | |
| }
 | |
| 
 | |
| /// If `place` is a field projection, and the field is being projected from a closure type,
 | |
| /// then returns the index of the field being projected. Note that this closure will always
 | |
| /// be `self` in the current MIR, because that is the only time we directly access the fields
 | |
| /// of a closure type.
 | |
| pub(crate) fn is_upvar_field_projection<'tcx>(
 | |
|     tcx: TyCtxt<'tcx>,
 | |
|     upvars: &[Upvar<'tcx>],
 | |
|     place_ref: PlaceRef<'tcx>,
 | |
|     body: &Body<'tcx>,
 | |
| ) -> Option<FieldIdx> {
 | |
|     let mut place_ref = place_ref;
 | |
|     let mut by_ref = false;
 | |
| 
 | |
|     if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() {
 | |
|         place_ref = place_base;
 | |
|         by_ref = true;
 | |
|     }
 | |
| 
 | |
|     match place_ref.last_projection() {
 | |
|         Some((place_base, ProjectionElem::Field(field, _ty))) => {
 | |
|             let base_ty = place_base.ty(body, tcx).ty;
 | |
|             if (base_ty.is_closure() || base_ty.is_generator())
 | |
|                 && (!by_ref || upvars[field.index()].by_ref)
 | |
|             {
 | |
|                 Some(field)
 | |
|             } else {
 | |
|                 None
 | |
|             }
 | |
|         }
 | |
|         _ => None,
 | |
|     }
 | |
| }
 | 
