@@ -6,7 +6,7 @@ use crate::{
6
6
use hir:: { def_id:: DefId , Body , HirId , HirIdMap } ;
7
7
use rustc_data_structures:: stable_set:: FxHashSet ;
8
8
use rustc_hir as hir;
9
- use rustc_middle:: hir:: place:: PlaceBase ;
9
+ use rustc_middle:: hir:: place:: { PlaceBase , Projection , ProjectionKind } ;
10
10
use rustc_middle:: ty:: { ParamEnv , TyCtxt } ;
11
11
12
12
pub ( super ) fn find_consumed_and_borrowed < ' a , ' tcx > (
@@ -114,12 +114,48 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
114
114
. borrowed
115
115
. insert ( TrackedValue :: from_place_with_projections_allowed ( place_with_id) ) ;
116
116
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.
119
122
//
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 ) {
123
159
self . places . borrowed_temporaries . insert ( place_with_id. hir_id ) ;
124
160
}
125
161
}
0 commit comments