Skip to content

Commit 22eaffe

Browse files
committed
Add comments with examples and tests
1 parent fb3b77a commit 22eaffe

32 files changed

+788
-136
lines changed

compiler/rustc_middle/src/ty/context.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,29 @@ pub struct TypeckResults<'tcx> {
431431
/// see `MinCaptureInformationMap` for more details.
432432
pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>,
433433

434-
pub closure_fake_reads: FxHashMap<DefId, Vec<(HirPlace<'tcx>, FakeReadCause)>>,
434+
/// Tracks the fake reads required for a closure and the reason for the fake read.
435+
/// When performing pattern matching for closures, there are times we don't end up
436+
/// reading places that are mentioned in a closure (because of _ patterns). However,
437+
/// to ensure the places are initialized, we introduce fake reads.
438+
/// Consider these two examples:
439+
/// ``` (discriminant matching with only wildcard arm)
440+
/// let x: u8;
441+
/// let c = || match x { _ => () };
442+
/// ```
443+
/// In this example, we don't need to actually read/borrow `x` in `c`, and so we don't
444+
/// want to capture it. However, we do still want an error here, because `x` should have
445+
/// to be initialized at the point where c is created. Therefore, we add a "fake read"
446+
/// instead.
447+
/// ``` (destructured assignments)
448+
/// let c = || {
449+
/// let (t1, t2) = t;
450+
/// }
451+
/// ```
452+
/// In the second example, we capture the disjoint fields of `t` (`t.0` & `t.1`), but
453+
/// we never capture `t`. This becomes an issue when we build MIR as we require
454+
/// information on `t` in order to create place `t.0` and `t.1`. We can solve this
455+
/// issue by fake reading `t`.
456+
pub closure_fake_reads: FxHashMap<DefId, Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>>,
435457

436458
/// Stores the type, expression, span and optional scope span of all types
437459
/// that are live across the yield of this generator (if a generator).

compiler/rustc_mir_build/src/build/expr/as_place.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,13 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
9090
let hir_projection = match mir_projection {
9191
ProjectionElem::Deref => HirProjectionKind::Deref,
9292
ProjectionElem::Field(field, _) => {
93-
// We will never encouter this for multivariant enums,
94-
// read the comment for `Downcast`.
9593
let variant = variant.unwrap_or(VariantIdx::new(0));
9694
HirProjectionKind::Field(field.index() as u32, variant)
9795
}
9896
ProjectionElem::Downcast(.., idx) => {
99-
// This projections exist for enums that have
100-
// single and multiple variants.
101-
// For single variants, enums are not captured completely.
97+
// We don't expect to see multi-variant enums here, as earlier
98+
// phases will have truncated them already. However, there can
99+
// still be downcasts, thanks to single-variant enums.
102100
// We keep track of VariantIdx so we can use this information
103101
// if the next ProjectionElem is a Field.
104102
variant = Some(*idx);
@@ -200,7 +198,7 @@ fn find_capture_matching_projections<'a, 'tcx>(
200198
/// Takes a PlaceBuilder and resolves the upvar (if any) within it, so that the
201199
/// `PlaceBuilder` now starts from `PlaceBase::Local`.
202200
///
203-
/// Returns a Result with the error being the HirId of the Upvar that was not found.
201+
/// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found.
204202
fn to_upvars_resolved_place_builder<'a, 'tcx>(
205203
from_builder: PlaceBuilder<'tcx>,
206204
tcx: TyCtxt<'tcx>,
@@ -305,15 +303,23 @@ impl<'tcx> PlaceBuilder<'tcx> {
305303
to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap()
306304
}
307305

306+
/// Attempts to resolve the `PlaceBuilder`.
307+
/// On success, it will return the resolved `PlaceBuilder`.
308+
/// On failure, it will return itself.
309+
///
310+
/// Upvars resolve may fail for a `PlaceBuilder` when attempting to
311+
/// resolve a disjoint field whose root variable is not captured
312+
/// (destructured assignments) or when attempting to resolve a root
313+
/// variable (discriminant matching with only wildcard arm) that is
314+
/// not captured. This can happen because the final mir that will be
315+
/// generated doesn't require a read for this place. Failures will only
316+
/// happen inside closures.
308317
crate fn try_upvars_resolved<'a>(
309318
self,
310319
tcx: TyCtxt<'tcx>,
311320
typeck_results: &'a ty::TypeckResults<'tcx>,
312321
) -> Result<PlaceBuilder<'tcx>, PlaceBuilder<'tcx>> {
313-
match to_upvars_resolved_place_builder(self, tcx, typeck_results) {
314-
Ok(upvars_resolved) => Ok(upvars_resolved),
315-
Err(upvars_unresolved) => Err(upvars_unresolved),
316-
}
322+
to_upvars_resolved_place_builder(self, tcx, typeck_results)
317323
}
318324

319325
crate fn base(&self) -> PlaceBase {
@@ -662,7 +668,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
662668
block,
663669
source_info,
664670
len,
665-
Rvalue::Len(slice.clone().into_place(self.tcx, self.typeck_results)),
671+
Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)),
666672
);
667673
// lt = idx < len
668674
self.cfg.push_assign(

compiler/rustc_mir_build/src/build/expr/as_rvalue.rs

+36-22
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
165165

166166
block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields))
167167
}
168-
ExprKind::Closure {
169-
closure_id,
170-
substs,
171-
upvars,
172-
movability,
173-
fake_reads: opt_fake_reads,
174-
} => {
168+
ExprKind::Closure { closure_id, substs, upvars, movability, fake_reads } => {
169+
// Convert the closure fake reads, if any, from `ExprRef` to mir `Place`
170+
// and push the fake reads.
171+
// This must come before creating the operands. This is required in case
172+
// there is a fake read and a borrow of the same path, since otherwise the
173+
// fake read might interfere with the borrow. Consider an example like this
174+
// one:
175+
// ```
176+
// let mut x = 0;
177+
// let c = || {
178+
// &mut x; // mutable borrow of `x`
179+
// match x { _ => () } // fake read of `x`
180+
// };
181+
// ```
182+
183+
// FIXME(RFC2229): Remove feature gate once diagnostics are improved
184+
if this.tcx.features().capture_disjoint_fields {
185+
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
186+
let place_builder =
187+
unpack!(block = this.as_place_builder(block, thir_place));
188+
189+
if let Ok(place_builder_resolved) =
190+
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
191+
{
192+
let mir_place =
193+
place_builder_resolved.into_place(this.tcx, this.typeck_results);
194+
this.cfg.push_fake_read(
195+
block,
196+
this.source_info(this.tcx.hir().span(hir_id)),
197+
cause,
198+
mir_place,
199+
);
200+
}
201+
}
202+
}
203+
175204
// see (*) above
176205
let operands: Vec<_> = upvars
177206
.into_iter()
@@ -210,21 +239,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
210239
}
211240
})
212241
.collect();
213-
if let Some(fake_reads) = opt_fake_reads {
214-
for (thir_place, cause) in fake_reads.into_iter() {
215-
let place_builder =
216-
unpack!(block = this.as_place_builder(block, thir_place));
217-
218-
if let Ok(place_builder_resolved) =
219-
place_builder.clone().try_upvars_resolved(this.tcx, this.typeck_results)
220-
{
221-
let mir_place = place_builder_resolved
222-
.clone()
223-
.into_place(this.tcx, this.typeck_results);
224-
this.cfg.push_fake_read(block, source_info, cause, mir_place);
225-
}
226-
}
227-
}
228242

229243
let result = match substs {
230244
UpvarSubsts::Generator(substs) => {

compiler/rustc_mir_build/src/build/matches/mod.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
146146
if let Ok(scrutinee_builder) =
147147
scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
148148
{
149-
let scrutinee_place =
150-
scrutinee_builder.clone().into_place(self.tcx, self.typeck_results);
149+
let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results);
151150
self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place);
152151
}
153152

@@ -245,7 +244,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
245244
let arm_source_info = self.source_info(arm.span);
246245
let arm_scope = (arm.scope, arm_source_info);
247246
self.in_scope(arm_scope, arm.lint_level, |this| {
248-
let body = arm.body.clone();
247+
let body = arm.body;
248+
249+
// `try_upvars_resolved` may fail if it is unable to resolve the given
250+
// `PlaceBuilder` inside a closure. In this case, we don't want to include
251+
// a scrutinee place. `scrutinee_place_builder` will fail to be resolved
252+
// if the only match arm is a wildcard (`_`).
253+
// Example:
254+
// ```
255+
// let foo = (0, 1);
256+
// let c = || {
257+
// match foo { _ => () };
258+
// };
259+
// ```
249260
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
250261
let scrutinee_place: Place<'tcx>;
251262
if let Ok(scrutinee_builder) = scrutinee_place_builder
@@ -496,6 +507,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
496507
VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
497508
)))) = self.local_decls[local].local_info
498509
{
510+
// `try_upvars_resolved` may fail if it is unable to resolve the given
511+
// `PlaceBuilder` inside a closure. In this case, we don't want to include
512+
// a scrutinee place. `scrutinee_place_builder` will fail for destructured
513+
// assignments. This is because a closure only captures the precise places
514+
// that it will read and as a result a closure may not capture the entire
515+
// tuple/struct and rather have individual places that will be read in the
516+
// final MIR.
517+
// Example:
518+
// ```
519+
// let foo = (0, 1);
520+
// let c = || {
521+
// let (v1, v2) = foo;
522+
// };
523+
// ```
499524
if let Ok(match_pair_resolved) =
500525
initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
501526
{

compiler/rustc_mir_build/src/build/matches/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
160160
if let Ok(test_place_builder) =
161161
place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results)
162162
{
163-
place = test_place_builder.clone().into_place(self.tcx, self.typeck_results);
163+
place = test_place_builder.into_place(self.tcx, self.typeck_results);
164164
} else {
165165
return;
166166
}

compiler/rustc_mir_build/src/thir/cx/expr.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -454,20 +454,20 @@ impl<'thir, 'tcx> Cx<'thir, 'tcx> {
454454
.map(|(captured_place, ty)| self.capture_upvar(expr, captured_place, ty)),
455455
);
456456

457+
// Convert the closure fake reads, if any, from hir `Place` to ExprRef
457458
let fake_reads = match self.typeck_results().closure_fake_reads.get(&def_id) {
458-
Some(vals) => Some(
459-
vals.iter()
460-
.map(|(place, cause)| {
461-
(
462-
self.arena.alloc(
463-
self.convert_captured_hir_place(expr, place.clone()),
464-
),
465-
*cause,
466-
)
467-
})
468-
.collect(),
469-
),
470-
None => None,
459+
Some(fake_reads) => fake_reads
460+
.iter()
461+
.map(|(place, cause, hir_id)| {
462+
(
463+
self.arena
464+
.alloc(self.convert_captured_hir_place(expr, place.clone())),
465+
*cause,
466+
*hir_id,
467+
)
468+
})
469+
.collect(),
470+
None => Vec::new(),
471471
};
472472

473473
ExprKind::Closure {

compiler/rustc_mir_build/src/thir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ pub enum ExprKind<'thir, 'tcx> {
281281
substs: UpvarSubsts<'tcx>,
282282
upvars: &'thir [Expr<'thir, 'tcx>],
283283
movability: Option<hir::Movability>,
284-
fake_reads: Option<Vec<(&'thir mut Expr<'thir, 'tcx>, FakeReadCause)>>,
284+
fake_reads: Vec<(&'thir mut Expr<'thir, 'tcx>, FakeReadCause, hir::HirId)>,
285285
},
286286
Literal {
287287
literal: &'tcx Const<'tcx>,

compiler/rustc_typeck/src/check/upvar.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
248248
let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter());
249249
self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type);
250250

251-
let fake_reads =
252-
delegate.fake_reads.into_iter().map(|(place, cause)| (place, cause)).collect();
251+
let fake_reads = delegate
252+
.fake_reads
253+
.into_iter()
254+
.map(|(place, cause, hir_id)| (place, cause, hir_id))
255+
.collect();
253256
self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads);
254257

255258
// If we are also inferred the closure kind here,
@@ -1154,7 +1157,7 @@ struct InferBorrowKind<'a, 'tcx> {
11541157
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
11551158
/// ```
11561159
capture_information: InferredCaptureInformation<'tcx>,
1157-
fake_reads: Vec<(Place<'tcx>, FakeReadCause)>,
1160+
fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>,
11581161
}
11591162

11601163
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
@@ -1416,9 +1419,9 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
14161419
}
14171420

14181421
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
1419-
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause) {
1422+
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) {
14201423
if let PlaceBase::Upvar(_) = place.base {
1421-
self.fake_reads.push((place, cause));
1424+
self.fake_reads.push((place, cause, diag_expr_id));
14221425
}
14231426
}
14241427

compiler/rustc_typeck/src/check/writeback.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -371,18 +371,18 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
371371
fn visit_fake_reads_map(&mut self) {
372372
let mut resolved_closure_fake_reads: FxHashMap<
373373
DefId,
374-
Vec<(HirPlace<'tcx>, FakeReadCause)>,
374+
Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>,
375375
> = Default::default();
376376
for (closure_def_id, fake_reads) in
377377
self.fcx.typeck_results.borrow().closure_fake_reads.iter()
378378
{
379-
let mut resolved_fake_reads = Vec::<(HirPlace<'tcx>, FakeReadCause)>::new();
380-
for (place, cause) in fake_reads.iter() {
379+
let mut resolved_fake_reads = Vec::<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>::new();
380+
for (place, cause, hir_id) in fake_reads.iter() {
381381
let locatable =
382382
self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local());
383383

384384
let resolved_fake_read = self.resolve(place.clone(), &locatable);
385-
resolved_fake_reads.push((resolved_fake_read, *cause));
385+
resolved_fake_reads.push((resolved_fake_read, *cause, *hir_id));
386386
}
387387
resolved_closure_fake_reads.insert(*closure_def_id, resolved_fake_reads);
388388
}

0 commit comments

Comments
 (0)