Skip to content

Commit

Permalink
Auto merge of rust-lang#54188 - lqd:fallout-53695, r=nikomatsakis
Browse files Browse the repository at this point in the history
NLL: disallow creation of immediately unusable variables

Fix rust-lang#53695

Original description follows

----

This WIP PR is for discussing the impact of fixing rust-lang#53695 by injecting a fake read in let patterns.

(Travis will fail, at least the `mir-opt` suite is failing in its current state)
  • Loading branch information
bors committed Sep 22, 2018
2 parents af50e38 + 3bdba74 commit 4591a24
Show file tree
Hide file tree
Showing 52 changed files with 185 additions and 102 deletions.
5 changes: 4 additions & 1 deletion src/librustc/ich/impls_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ for mir::StatementKind<'gcx> {
place.hash_stable(hcx, hasher);
rvalue.hash_stable(hcx, hasher);
}
mir::StatementKind::ReadForMatch(ref place) => {
mir::StatementKind::FakeRead(ref cause, ref place) => {
cause.hash_stable(hcx, hasher);
place.hash_stable(hcx, hasher);
}
mir::StatementKind::SetDiscriminant { ref place, variant_index } => {
Expand Down Expand Up @@ -271,6 +272,8 @@ for mir::StatementKind<'gcx> {
}
}

impl_stable_hash_for!(enum mir::FakeReadCause { ForMatch, ForLet });

impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>>
for mir::ValidationOperand<'gcx, T>
where T: HashStable<StableHashingContext<'a>>
Expand Down
36 changes: 32 additions & 4 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1613,8 +1613,10 @@ pub enum StatementKind<'tcx> {
Assign(Place<'tcx>, Rvalue<'tcx>),

/// This represents all the reading that a pattern match may do
/// (e.g. inspecting constants and discriminant values).
ReadForMatch(Place<'tcx>),
/// (e.g. inspecting constants and discriminant values), and the
/// kind of pattern it comes from. This is in order to adapt potential
/// error messages to these specific patterns.
FakeRead(FakeReadCause, Place<'tcx>),

/// Write the discriminant for a variant to the enum Place.
SetDiscriminant {
Expand Down Expand Up @@ -1662,6 +1664,31 @@ pub enum StatementKind<'tcx> {
Nop,
}

/// The `FakeReadCause` describes the type of pattern why a `FakeRead` statement exists.
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug)]
pub enum FakeReadCause {
/// Inject a fake read of the borrowed input at the start of each arm's
/// pattern testing code.
///
/// This should ensure that you cannot change the variant for an enum
/// while you are in the midst of matching on it.
ForMatch,

/// Officially, the semantics of
///
/// `let pattern = <expr>;`
///
/// is that `<expr>` is evaluated into a temporary and then this temporary is
/// into the pattern.
///
/// However, if we see the simple pattern `let var = <expr>`, we optimize this to
/// evaluate `<expr>` directly into the variable `var`. This is mostly unobservable,
/// 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,
}

/// The `ValidationOp` describes what happens with each of the operands of a
/// `Validate` statement.
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, PartialEq, Eq)]
Expand Down Expand Up @@ -1718,7 +1745,7 @@ impl<'tcx> Debug for Statement<'tcx> {
use self::StatementKind::*;
match self.kind {
Assign(ref place, ref rv) => write!(fmt, "{:?} = {:?}", place, rv),
ReadForMatch(ref place) => write!(fmt, "ReadForMatch({:?})", place),
FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place),
// (reuse lifetime rendering policy from ppaux.)
EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
Validate(ref op, ref places) => write!(fmt, "Validate({:?}, {:?})", op, places),
Expand Down Expand Up @@ -2585,6 +2612,7 @@ CloneTypeFoldableAndLiftImpls! {
Mutability,
SourceInfo,
UpvarDecl,
FakeReadCause,
ValidationOp,
SourceScope,
SourceScopeData,
Expand Down Expand Up @@ -2651,7 +2679,7 @@ BraceStructTypeFoldableImpl! {
EnumTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for StatementKind<'tcx> {
(StatementKind::Assign)(a, b),
(StatementKind::ReadForMatch)(place),
(StatementKind::FakeRead)(cause, place),
(StatementKind::SetDiscriminant) { place, variant_index },
(StatementKind::StorageLive)(a),
(StatementKind::StorageDead)(a),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ macro_rules! make_mir_visitor {
ref $($mutability)* rvalue) => {
self.visit_assign(block, place, rvalue, location);
}
StatementKind::ReadForMatch(ref $($mutability)* place) => {
StatementKind::FakeRead(_, ref $($mutability)* place) => {
self.visit_place(place,
PlaceContext::Inspect,
location);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED],
"disable user provided type assertion in NLL"),
nll_dont_emit_read_for_match: bool = (false, parse_bool, [UNTRACKED],
"in match codegen, do not include ReadForMatch statements (used by mir-borrowck)"),
"in match codegen, do not include FakeRead statements (used by mir-borrowck)"),
dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
"emit diagnostics rather than buffering (breaks NLL error downgrading, sorting)."),
polonius: bool = (false, parse_bool, [UNTRACKED],
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1505,7 +1505,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.emit_read_for_match()
}

/// If true, make MIR codegen for `match` emit ReadForMatch
/// If true, make MIR codegen for `match` emit FakeRead
/// statements (which simulate the maximal effect of executing the
/// patterns in a match arm).
pub fn emit_read_for_match(&self) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_codegen_llvm/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
bx
}
mir::StatementKind::ReadForMatch(_) |
mir::StatementKind::FakeRead(..) |
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Validate(..) |
mir::StatementKind::AscribeUserType(..) |
Expand Down
17 changes: 16 additions & 1 deletion src/librustc_mir/borrow_check/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use borrow_check::WriteKind;
use rustc::middle::region::ScopeTree;
use rustc::mir::VarBindingForm;
use rustc::mir::{BindingForm, BorrowKind, ClearCrossCrate, Field, Local};
use rustc::mir::{LocalDecl, LocalKind, Location, Operand, Place};
use rustc::mir::{FakeReadCause, LocalDecl, LocalKind, Location, Operand, Place};
use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind};
use rustc::ty;
use rustc_data_structures::fx::FxHashSet;
Expand Down Expand Up @@ -1020,6 +1020,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
false
}
}

/// Returns the `FakeReadCause` at this location if it is a `FakeRead` statement.
pub(super) fn retrieve_fake_read_cause_for_location(
&self,
location: &Location,
) -> Option<FakeReadCause> {
let stmt = self.mir.basic_blocks()[location.block]
.statements
.get(location.statement_index)?;
if let StatementKind::FakeRead(cause, _) = stmt.kind {
Some(cause)
} else {
None
}
}
}

// The span(s) associated to a use of a place.
Expand Down
6 changes: 3 additions & 3 deletions src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,9 +501,9 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
flow_state,
);
}
StatementKind::ReadForMatch(ref place) => {
StatementKind::FakeRead(_, ref place) => {
self.access_place(
ContextKind::ReadForMatch.new(location),
ContextKind::FakeRead.new(location),
(place, span),
(Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
LocalMutationIsAllowed::No,
Expand Down Expand Up @@ -2229,7 +2229,7 @@ enum ContextKind {
CallDest,
Assert,
Yield,
ReadForMatch,
FakeRead,
StorageDead,
}

Expand Down
10 changes: 8 additions & 2 deletions src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use borrow_check::borrow_set::BorrowData;
use borrow_check::nll::region_infer::Cause;
use borrow_check::{Context, MirBorrowckCtxt, WriteKind};
use rustc::mir::{Local, Location, Place, TerminatorKind};
use rustc::mir::{FakeReadCause, Local, Location, Place, TerminatorKind};
use rustc_errors::DiagnosticBuilder;
use rustc::ty::Region;

Expand Down Expand Up @@ -142,7 +142,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
if spans.for_closure() {
"borrow later captured here by closure"
} else {
"borrow later used here"
// Check if the location represents a `FakeRead`, and adapt the error
// message to the `FakeReadCause` it is from: in particular,
// the ones inserted in optimized `let var = <expr>` patterns.
match self.retrieve_fake_read_cause_for_location(&location) {
Some(FakeReadCause::ForLet) => "borrow later stored here",
_ => "borrow later used here"
}
}
};
err.span_label(spans.var_or_use(), message);
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/borrow_check/nll/invalidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc
JustWrite
);
}
StatementKind::ReadForMatch(ref place) => {
StatementKind::FakeRead(_, ref place) => {
self.access_place(
ContextKind::ReadForMatch.new(location),
ContextKind::FakeRead.new(location),
place,
(Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
LocalMutationIsAllowed::No,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
);
}
}
StatementKind::ReadForMatch(_)
StatementKind::FakeRead(..)
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::InlineAsm { .. }
Expand Down
34 changes: 26 additions & 8 deletions src/librustc_mir/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if let (true, Some(borrow_temp)) =
(tcx.emit_read_for_match(), borrowed_input_temp.clone())
{
// inject a fake read of the borrowed input at
// the start of each arm's pattern testing
// code.
//
// This should ensure that you cannot change
// the variant for an enum while you are in
// the midst of matching on it.
// Inject a fake read, see comments on `FakeReadCause::ForMatch`.
let pattern_source_info = self.source_info(pattern.span);
self.cfg.push(
*pre_binding_block,
Statement {
source_info: pattern_source_info,
kind: StatementKind::ReadForMatch(borrow_temp.clone()),
kind: StatementKind::FakeRead(
FakeReadCause::ForMatch,
borrow_temp.clone(),
),
},
);
}
Expand Down Expand Up @@ -264,6 +261,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
unpack!(block = self.into(&place, block, initializer));


// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let source_info = self.source_info(irrefutable_pat.span);
self.cfg.push(
block,
Statement {
source_info,
kind: StatementKind::FakeRead(FakeReadCause::ForLet, place.clone()),
},
);

self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
block.unit()
}
Expand Down Expand Up @@ -305,6 +314,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
},
);

// Inject a fake read, see comments on `FakeReadCause::ForLet`.
self.cfg.push(
block,
Statement {
source_info,
kind: StatementKind::FakeRead(FakeReadCause::ForLet, place.clone()),
},
);

self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
block.unit()
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/dataflow/impls/borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
}
}

mir::StatementKind::ReadForMatch(..) |
mir::StatementKind::FakeRead(..) |
mir::StatementKind::SetDiscriminant { .. } |
mir::StatementKind::StorageLive(..) |
mir::StatementKind::Validate(..) |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/dataflow/move_paths/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
}
self.gather_rvalue(rval);
}
StatementKind::ReadForMatch(ref place) => {
StatementKind::FakeRead(_, ref place) => {
self.create_move_path(place);
}
StatementKind::InlineAsm { ref outputs, ref inputs, ref asm } => {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/interpret/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
self.deallocate_local(old_val)?;
}

// No dynamic semantics attached to `ReadForMatch`; MIR
// No dynamic semantics attached to `FakeRead`; MIR
// interpreter is solely intended for borrowck'ed code.
ReadForMatch(..) => {}
FakeRead(..) => {}

// Validity checks.
Validate(op, ref places) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
self.source_info = statement.source_info;
match statement.kind {
StatementKind::Assign(..) |
StatementKind::ReadForMatch(..) |
StatementKind::FakeRead(..) |
StatementKind::SetDiscriminant { .. } |
StatementKind::StorageLive(..) |
StatementKind::StorageDead(..) |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
StatementKind::Assign(ref place, ref rvalue) => {
this.visit_assign(bb, place, rvalue, location);
}
StatementKind::ReadForMatch(..) |
StatementKind::FakeRead(..) |
StatementKind::SetDiscriminant { .. } |
StatementKind::StorageLive(_) |
StatementKind::StorageDead(_) |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ fn check_statement(
check_rvalue(tcx, mir, rval, span)
}

StatementKind::ReadForMatch(_) => Err((span, "match in const fn is unstable".into())),
StatementKind::FakeRead(..) => Err((span, "match in const fn is unstable".into())),

// just an assignment
StatementKind::SetDiscriminant { .. } => Ok(()),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/remove_noop_landing_pads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl RemoveNoopLandingPads {
) -> bool {
for stmt in &mir[bb].statements {
match stmt.kind {
StatementKind::ReadForMatch(_) |
StatementKind::FakeRead(..) |
StatementKind::StorageLive(_) |
StatementKind::StorageDead(_) |
StatementKind::EndRegion(_) |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/rustc_peek.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir::StatementKind::Assign(ref place, ref rvalue) => {
(place, rvalue)
}
mir::StatementKind::ReadForMatch(_) |
mir::StatementKind::FakeRead(..) |
mir::StatementKind::StorageLive(_) |
mir::StatementKind::StorageDead(_) |
mir::StatementKind::InlineAsm { .. } |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_passes/mir_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
self.record("Statement", statement);
self.record(match statement.kind {
StatementKind::Assign(..) => "StatementKind::Assign",
StatementKind::ReadForMatch(..) => "StatementKind::ReadForMatch",
StatementKind::FakeRead(..) => "StatementKind::FakeRead",
StatementKind::EndRegion(..) => "StatementKind::EndRegion",
StatementKind::Validate(..) => "StatementKind::Validate",
StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
Expand Down
2 changes: 2 additions & 0 deletions src/test/mir-opt/basic_assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fn main() {
// bb0: {
// StorageLive(_1);
// _1 = const false;
// FakeRead(ForLet, _1);
// StorageLive(_2);
// StorageLive(_3);
// _3 = _1;
Expand All @@ -55,6 +56,7 @@ fn main() {
// StorageLive(_4);
// _4 = std::option::Option<std::boxed::Box<u32>>::None;
// AscribeUserType(_4, o, Canonical { variables: [], value: std::option::Option<std::boxed::Box<u32>> });
// FakeRead(ForLet, _4);
// StorageLive(_5);
// StorageLive(_6);
// _6 = move _4;
Expand Down
1 change: 1 addition & 0 deletions src/test/mir-opt/box_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ impl Drop for S {
//
// bb4: {
// StorageDead(_2);
// FakeRead(ForLet, _1);
// StorageLive(_4);
// _4 = move _1;
// _3 = const std::mem::drop(move _4) -> [return: bb5, unwind: bb7];
Expand Down
Loading

0 comments on commit 4591a24

Please sign in to comment.