@@ -6,7 +6,7 @@ use crate::{
66use hir:: { def_id:: DefId , Body , HirId , HirIdMap } ;
77use rustc_data_structures:: stable_set:: FxHashSet ;
88use rustc_hir as hir;
9- use rustc_middle:: hir:: place:: PlaceBase ;
9+ use rustc_middle:: hir:: place:: { PlaceBase , Projection , ProjectionKind } ;
1010use rustc_middle:: ty:: { ParamEnv , TyCtxt } ;
1111
1212pub ( super ) fn find_consumed_and_borrowed < ' a , ' tcx > (
@@ -114,12 +114,48 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
114114 . borrowed
115115 . insert ( TrackedValue :: from_place_with_projections_allowed ( place_with_id) ) ;
116116
117- // Keep track of whether this is a borrowed temporary (i.e. a borrow of an RValue)
118- // so that later in generator_interior we can use the correct scope.
117+ // Ordinarily a value is consumed by it's parent, but in the special case of a
118+ // borrowed RValue, we create a reference that lives as long as the temporary scope
119+ // for that expression (typically, the innermost statement, but sometimes the enclosing
120+ // block). We record this fact here so that later in generator_interior
121+ // we can use the correct scope.
119122 //
120- // We ignore borrows that are the result of an autoref because these will be
121- // immediately consumed and should not extend the temporary's lifetime.
122- if let ( false , PlaceBase :: Rvalue ) = ( is_autoref, place_with_id. place . base ) {
123+ // We special case borrows through a dereference (`&*x`, `&mut *x` where `x` is
124+ // some rvalue expression), since these are essentially a copy of a pointer.
125+ // In other words, this borrow does not refer to the
126+ // temporary (`*x`), but to the referent (whatever `x` is a borrow of).
127+ //
128+ // We were considering that we might encounter problems down the line if somehow,
129+ // some part of the compiler were to look at this result and try to use it to
130+ // drive a borrowck-like analysis (this does not currently happen, as of this writing).
131+ // But even this should be fine, because the lifetime of the dereferenced reference
132+ // found in the rvalue is only significant as an intermediate 'link' to the value we
133+ // are producing, and we separately track whether that value is live over a yield.
134+ // Example:
135+ //
136+ // ```notrust
137+ // fn identity<T>(x: &mut T) -> &mut T { x }
138+ // let a: A = ...;
139+ // let y: &'y mut A = &mut *identity(&'a mut a);
140+ // ^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow we are talking about
141+ // ```
142+ //
143+ // The expression `*identity(...)` is a deref of an rvalue,
144+ // where the `identity(...)` (the rvalue) produces a return type
145+ // of `&'rv mut A`, where `'a: 'rv`. We then assign this result to
146+ // `'y`, resulting in (transitively) `'a: 'y` (i.e., while `y` is in use,
147+ // `a` will be considered borrowed). Other parts of the code will ensure
148+ // that if `y` is live over a yield, `&'y mut A` appears in the generator
149+ // state. If `'y` is live, then any sound region analysis must conclude
150+ // that `'a` is also live. So if this causes a bug, blame some other
151+ // part of the code!
152+ let is_deref = place_with_id
153+ . place
154+ . projections
155+ . iter ( )
156+ . any ( |Projection { kind, .. } | * kind == ProjectionKind :: Deref ) ;
157+
158+ if let ( false , PlaceBase :: Rvalue ) = ( is_deref, place_with_id. place . base ) {
123159 self . places . borrowed_temporaries . insert ( place_with_id. hir_id ) ;
124160 }
125161 }
0 commit comments