Skip to content

Commit

Permalink
Rollup merge of rust-lang#83521 - sexxi-goose:quick-diagnostic-fix, r…
Browse files Browse the repository at this point in the history
…=nikomatsakis

2229: Fix diagnostic issue when using FakeReads in closures

This PR fixes a diagnostic issue caused by rust-lang#82536. A temporary work around was used in this merged PR which involved feature gating the addition of FakeReads introduced as a result of pattern matching in closures.

The fix involves adding an optional closure DefId to ForLet and ForMatchedPlace FakeReadCauses. This DefId will only be added if a closure pattern matches a Place starting with an Upvar.

r? `@nikomatsakis`
  • Loading branch information
Dylan-DPC authored Apr 4, 2021
2 parents 83eb29a + c29dc12 commit 7124e29
Show file tree
Hide file tree
Showing 39 changed files with 133 additions and 92 deletions.
20 changes: 16 additions & 4 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1482,7 +1482,7 @@ pub enum StatementKind<'tcx> {
///
/// Note that this also is emitted for regular `let` bindings to ensure that locals that are
/// never accessed still get some sanity checks for, e.g., `let x: ! = ..;`
FakeRead(FakeReadCause, Box<Place<'tcx>>),
FakeRead(Box<(FakeReadCause, Place<'tcx>)>),

/// Write the discriminant for a variant to the enum Place.
SetDiscriminant { place: Box<Place<'tcx>>, variant_index: VariantIdx },
Expand Down Expand Up @@ -1575,7 +1575,12 @@ pub enum FakeReadCause {

/// `let x: !; match x {}` doesn't generate any read of x so we need to
/// generate a read of x to check that it is initialized and safe.
ForMatchedPlace,
///
/// If a closure pattern matches a Place starting with an Upvar, then we introduce a
/// FakeRead for that Place outside the closure, in such a case this option would be
/// Some(closure_def_id).
/// Otherwise, the value of the optional DefId will be None.
ForMatchedPlace(Option<DefId>),

/// A fake read of the RefWithinGuard version of a bind-by-value variable
/// in a match guard to ensure that it's value hasn't change by the time
Expand All @@ -1594,7 +1599,12 @@ pub enum FakeReadCause {
/// but in some cases it can affect the borrow checker, as in #53695.
/// Therefore, we insert a "fake read" here to ensure that we get
/// appropriate errors.
ForLet,
///
/// If a closure pattern matches a Place starting with an Upvar, then we introduce a
/// FakeRead for that Place outside the closure, in such a case this option would be
/// Some(closure_def_id).
/// Otherwise, the value of the optional DefId will be None.
ForLet(Option<DefId>),

/// If we have an index expression like
///
Expand All @@ -1618,7 +1628,9 @@ impl Debug for Statement<'_> {
use self::StatementKind::*;
match self.kind {
Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv),
FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place),
FakeRead(box (ref cause, ref place)) => {
write!(fmt, "FakeRead({:?}, {:?})", cause, place)
}
Retag(ref kind, ref place) => write!(
fmt,
"Retag({}{:?})",
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ macro_rules! make_mir_visitor {
) => {
self.visit_assign(place, rvalue, location);
}
StatementKind::FakeRead(_, place) => {
StatementKind::FakeRead(box (_, place)) => {
self.visit_place(
place,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1728,7 +1728,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
match statement {
Statement { kind: StatementKind::FakeRead(cause, box place), .. }
Statement { kind: StatementKind::FakeRead(box (cause, place)), .. }
if *place == self.place =>
{
self.cause = Some(*cause);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let block = &self.body.basic_blocks()[location.block];

let kind = if let Some(&Statement {
kind: StatementKind::FakeRead(FakeReadCause::ForLet, _),
kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)),
..
}) = block.statements.get(location.statement_index)
{
Expand Down
22 changes: 20 additions & 2 deletions compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItemGroup;
use rustc_hir::GeneratorKind;
use rustc_middle::mir::{
AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand, Place,
PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
Expand Down Expand Up @@ -795,6 +795,24 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}

// StatementKind::FakeRead only contains a def_id if they are introduced as a result
// of pattern matching within a closure.
if let StatementKind::FakeRead(box (cause, ref place)) = stmt.kind {
match cause {
FakeReadCause::ForMatchedPlace(Some(closure_def_id))
| FakeReadCause::ForLet(Some(closure_def_id)) => {
debug!("move_spans: def_id={:?} place={:?}", closure_def_id, place);
let places = &[Operand::Move(*place)];
if let Some((args_span, generator_kind, var_span)) =
self.closure_span(closure_def_id, moved_place, places)
{
return ClosureUse { generator_kind, args_span, var_span };
}
}
_ => {}
}
}

let normal_ret =
if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
PatUse(stmt.source_info.span)
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/borrow_check/invalidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {

self.mutate_place(location, *lhs, Shallow(None), JustWrite);
}
StatementKind::FakeRead(_, _) => {
StatementKind::FakeRead(box (_, _)) => {
// Only relevant for initialized/liveness/safety checks.
}
StatementKind::SetDiscriminant { place, variant_index: _ } => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc

self.mutate_place(location, (*lhs, span), Shallow(None), JustWrite, flow_state);
}
StatementKind::FakeRead(_, box ref place) => {
StatementKind::FakeRead(box (_, ref place)) => {
// Read for match doesn't access any memory and is used to
// assert that a place is safe and live. So we don't have to
// do any checks here.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir/src/dataflow/move_paths/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
}
self.gather_rvalue(rval);
}
StatementKind::FakeRead(_, place) => {
self.create_move_path(**place);
StatementKind::FakeRead(box (_, place)) => {
self.create_move_path(*place);
}
StatementKind::LlvmInlineAsm(ref asm) => {
for (output, kind) in iter::zip(&*asm.outputs, &asm.asm.outputs) {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir/src/transform/coverage/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,10 +683,10 @@ pub(super) fn filtered_statement_span(
// and `_1` is the `Place` for `somenum`.
//
// If and when the Issue is resolved, remove this special case match pattern:
StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None,
StatementKind::FakeRead(box (cause, _)) if cause == FakeReadCause::ForGuardBinding => None,

// Retain spans from all other statements
StatementKind::FakeRead(_, _) // Not including `ForGuardBinding`
StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
| StatementKind::CopyNonOverlapping(..)
| StatementKind::Assign(_)
| StatementKind::SetDiscriminant { .. }
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/build/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl<'tcx> CFG<'tcx> {
cause: FakeReadCause,
place: Place<'tcx>,
) {
let kind = StatementKind::FakeRead(cause, box place);
let kind = StatementKind::FakeRead(box (cause, place));
let stmt = Statement { source_info, kind };
self.push(block, stmt);
}
Expand Down
32 changes: 14 additions & 18 deletions compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,24 +179,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// match x { _ => () } // fake read of `x`
// };
// ```
// FIXME(RFC2229): Remove feature gate once diagnostics are improved
if this.tcx.features().capture_disjoint_fields {
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
let place_builder =
unpack!(block = this.as_place_builder(block, thir_place));

if let Ok(place_builder_resolved) =
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
{
let mir_place =
place_builder_resolved.into_place(this.tcx, this.typeck_results);
this.cfg.push_fake_read(
block,
this.source_info(this.tcx.hir().span(*hir_id)),
*cause,
mir_place,
);
}
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
let place_builder = unpack!(block = this.as_place_builder(block, thir_place));

if let Ok(place_builder_resolved) =
place_builder.try_upvars_resolved(this.tcx, this.typeck_results)
{
let mir_place =
place_builder_resolved.into_place(this.tcx, this.typeck_results);
this.cfg.push_fake_read(
block,
this.source_info(this.tcx.hir().span(*hir_id)),
*cause,
mir_place,
);
}
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// uninhabited value. If we get never patterns, those will check that
// the place is initialized, and so this read would only be used to
// check safety.
let cause_matched_place = FakeReadCause::ForMatchedPlace;
let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
let source_info = self.source_info(scrutinee_span);

if let Ok(scrutinee_builder) =
Expand Down Expand Up @@ -400,7 +400,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let source_info = self.source_info(irrefutable_pat.span);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet, place);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);

self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
block.unit()
Expand Down Expand Up @@ -435,7 +435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let pattern_source_info = self.source_info(irrefutable_pat.span);
let cause_let = FakeReadCause::ForLet;
let cause_let = FakeReadCause::ForLet(None);
self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);

let ty_source_info = self.source_info(user_ty_span);
Expand Down
21 changes: 18 additions & 3 deletions compiler/rustc_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
if needs_to_be_read {
self.borrow_expr(&discr, ty::ImmBorrow);
} else {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};

self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForMatchedPlace,
FakeReadCause::ForMatchedPlace(closure_def_id),
discr_place.hir_id,
);

Expand Down Expand Up @@ -578,9 +583,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
}

fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};

self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForMatchedPlace,
FakeReadCause::ForMatchedPlace(closure_def_id),
discr_place.hir_id,
);
self.walk_pat(discr_place, &arm.pat);
Expand All @@ -595,9 +605,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
/// let binding, and *not* a match arm or nested pat.)
fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
let closure_def_id = match discr_place.place.base {
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id.to_def_id()),
_ => None,
};

self.delegate.fake_read(
discr_place.place.clone(),
FakeReadCause::ForLet,
FakeReadCause::ForLet(closure_def_id),
discr_place.hir_id,
);
self.walk_pat(discr_place, pat);
Expand Down
Loading

0 comments on commit 7124e29

Please sign in to comment.