Skip to content

Commit 12d8ca1

Browse files
eholknikomatsakis
andcommitted
Use projections rather than is_autoref
Also includes a lengthy comment arguing the correctness. Co-authored-by: Niko Matsakis <niko@alum.mit.edu>
1 parent 170b027 commit 12d8ca1

File tree

1 file changed

+42
-6
lines changed

1 file changed

+42
-6
lines changed

compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs

+42-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
use hir::{def_id::DefId, Body, HirId, HirIdMap};
77
use rustc_data_structures::stable_set::FxHashSet;
88
use rustc_hir as hir;
9-
use rustc_middle::hir::place::PlaceBase;
9+
use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind};
1010
use rustc_middle::ty::{ParamEnv, TyCtxt};
1111

1212
pub(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

Comments
 (0)