diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index efde224c3e12..f410865a6cd7 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -21,6 +21,7 @@ use rustc_data_structures::control_flow_graph::ControlFlowGraph; use rustc_serialize as serialize; use hir::def::CtorKind; use hir::def_id::DefId; +use mir::visit::MirVisitable; use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; @@ -868,6 +869,14 @@ impl<'tcx> BasicBlockData<'tcx> { } } } + + pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> { + if index < self.statements.len() { + &self.statements[index] + } else { + &self.terminator + } + } } impl<'tcx> Debug for TerminatorKind<'tcx> { diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 971b5eb22752..bc1b3edbb6ad 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -14,7 +14,6 @@ use rustc::mir::{BorrowKind, Field, Local, Location, Operand}; use rustc::mir::{Place, ProjectionElem, Rvalue, Statement, StatementKind}; use rustc::ty::{self, RegionKind}; use rustc_data_structures::indexed_vec::Idx; -use rustc_errors::DiagnosticBuilder; use std::rc::Rc; @@ -134,19 +133,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } - fn explain_why_borrow_contains_point( - &self, - context: Context, - borrow: &BorrowData<'_>, - err: &mut DiagnosticBuilder<'_>, - ) { - if let Some(regioncx) = &self.nonlexical_regioncx { - if let Some(cause) = regioncx.why_region_contains_point(borrow.region, context.loc) { - cause.label_diagnostic(self.mir, err); - } - } - } - /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of /// the local assigned at `location`. /// This is done by searching in statements succeeding `location` diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs new file mode 100644 index 000000000000..bfb7e793eee0 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -0,0 +1,210 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use borrow_check::{Context, MirBorrowckCtxt}; +use borrow_check::nll::region_infer::{Cause, RegionInferenceContext}; +use dataflow::BorrowData; +use rustc::mir::{Local, Location, Mir}; +use rustc::mir::visit::{MirVisitable, PlaceContext, Visitor}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::DiagnosticBuilder; +use util::liveness::{self, DefUse, LivenessMode}; + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + pub(in borrow_check) fn explain_why_borrow_contains_point( + &self, + context: Context, + borrow: &BorrowData<'_>, + err: &mut DiagnosticBuilder<'_>, + ) { + if let Some(regioncx) = &self.nonlexical_regioncx { + if let Some(cause) = regioncx.why_region_contains_point(borrow.region, context.loc) { + let mir = self.mir; + + cause.label_diagnostic(mir, err); + + match *cause.root_cause() { + Cause::LiveVar(local, location) => { + match find_regular_use(&mir, regioncx, borrow, location, local) { + Some(p) => { + err.span_label( + mir.source_info(p).span, + format!("borrow later used here"), + ); + } + + None => { + span_bug!( + mir.source_info(context.loc).span, + "Cause should end in a LiveVar" + ); + } + } + } + + Cause::DropVar(local, location) => { + match find_drop_use(&mir, regioncx, borrow, location, local) { + Some(p) => { + let local_name = &mir.local_decls[local].name.unwrap(); + + err.span_label( + mir.source_info(p).span, + format!( + "borrow later used here, when `{}` is dropped", + local_name + ), + ); + } + + None => { + span_bug!( + mir.source_info(context.loc).span, + "Cause should end in a DropVar" + ); + } + } + } + + _ => (), + } + } + } + } +} + +fn find_regular_use<'gcx, 'tcx>( + mir: &'gcx Mir, + regioncx: &'tcx RegionInferenceContext, + borrow: &'tcx BorrowData, + start_point: Location, + local: Local, +) -> Option { + let mut uf = UseFinder { + mir, + regioncx, + borrow, + start_point, + local, + liveness_mode: LivenessMode { + include_regular_use: true, + include_drops: false, + }, + }; + + uf.find() +} + +fn find_drop_use<'gcx, 'tcx>( + mir: &'gcx Mir, + regioncx: &'tcx RegionInferenceContext, + borrow: &'tcx BorrowData, + start_point: Location, + local: Local, +) -> Option { + let mut uf = UseFinder { + mir, + regioncx, + borrow, + start_point, + local, + liveness_mode: LivenessMode { + include_regular_use: false, + include_drops: true, + }, + }; + + uf.find() +} + +struct UseFinder<'gcx, 'tcx> { + mir: &'gcx Mir<'gcx>, + regioncx: &'tcx RegionInferenceContext<'tcx>, + borrow: &'tcx BorrowData<'tcx>, + start_point: Location, + local: Local, + liveness_mode: LivenessMode, +} + +impl<'gcx, 'tcx> UseFinder<'gcx, 'tcx> { + fn find(&mut self) -> Option { + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(self.start_point); + while let Some(p) = stack.pop() { + if !self.regioncx.region_contains_point(self.borrow.region, p) { + continue; + } + + if !visited.insert(p) { + continue; + } + + let block_data = &self.mir[p.block]; + let (defined, used) = self.def_use(p, block_data.visitable(p.statement_index)); + + if used { + return Some(p); + } else if !defined { + if p.statement_index < block_data.statements.len() { + stack.push(Location { + statement_index: p.statement_index + 1, + ..p + }); + } else { + stack.extend( + block_data + .terminator() + .successors() + .iter() + .map(|&basic_block| Location { + statement_index: 0, + block: basic_block, + }), + ); + } + } + } + + None + } + + fn def_use(&self, location: Location, thing: &MirVisitable<'tcx>) -> (bool, bool) { + let mut visitor = DefUseVisitor { + defined: false, + used: false, + local: self.local, + liveness_mode: self.liveness_mode, + }; + + thing.apply(location, &mut visitor); + + (visitor.defined, visitor.used) + } +} + +struct DefUseVisitor { + defined: bool, + used: bool, + local: Local, + liveness_mode: LivenessMode, +} + +impl<'tcx> Visitor<'tcx> for DefUseVisitor { + fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) { + if local == self.local { + match liveness::categorize(context, self.liveness_mode) { + Some(DefUse::Def) => self.defined = true, + Some(DefUse::Use) => self.used = true, + None => (), + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 98a74f06703d..f96e107efa38 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -27,6 +27,7 @@ use util::pretty::{self, ALIGN}; use self::mir_util::PassWhere; mod constraint_generation; +pub mod explain_borrow; pub(crate) mod region_infer; mod renumber; mod subtype_constraint_generation; diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 6f3e6cb1ec51..5e921cc330d8 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1229,4 +1229,22 @@ impl Cause { } } } + + pub(crate) fn root_cause(&self) -> &Cause { + match self { + Cause::LiveVar(..) | + Cause::DropVar(..) | + Cause::LiveOther(..) | + Cause::UniversalRegion(..) => { + self + } + + Cause::Outlives { + original_cause, + .. + } => { + original_cause.root_cause() + } + } + } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 4d26230e0612..a7efdaf08264 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -23,6 +23,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(decl_macro)] +#![feature(dyn_trait)] #![feature(i128_type)] #![feature(inclusive_range_syntax)] #![feature(inclusive_range)] diff --git a/src/test/ui/nll/capture-ref-in-struct.rs b/src/test/ui/nll/capture-ref-in-struct.rs index 5eae5d924712..6e234a966d14 100644 --- a/src/test/ui/nll/capture-ref-in-struct.rs +++ b/src/test/ui/nll/capture-ref-in-struct.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:-Znll -Zborrowck=mir +// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause // Test that a structure which tries to store a pointer to `y` into // `p` (indirectly) fails to compile. diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr index 3812ff404079..7b3f7d25dd03 100644 --- a/src/test/ui/nll/capture-ref-in-struct.stderr +++ b/src/test/ui/nll/capture-ref-in-struct.stderr @@ -6,6 +6,9 @@ error[E0597]: `y` does not live long enough ... 37 | } | - borrowed value only lives until here +38 | +39 | deref(p); + | - borrow later used here | = note: borrowed value must be valid for lifetime '_#5r... diff --git a/src/test/ui/nll/closure-requirements/escape-argument.rs b/src/test/ui/nll/closure-requirements/escape-argument.rs index 7e918c6431de..17fadf0a2978 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.rs +++ b/src/test/ui/nll/closure-requirements/escape-argument.rs @@ -22,7 +22,7 @@ // basically checking that the MIR type checker correctly enforces the // closure signature. -// compile-flags:-Znll -Zborrowck=mir -Zverbose +// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose #![feature(rustc_attrs)] diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 940197986001..09d5617b08ef 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -31,6 +31,9 @@ error[E0597]: `y` does not live long enough 38 | //~^ ERROR `y` does not live long enough [E0597] 39 | } | - borrowed value only lives until here +40 | +41 | deref(p); + | - borrow later used here | = note: borrowed value must be valid for lifetime '_#6r... diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs index 05700ae00ad4..984c9fe7c34b 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs @@ -15,7 +15,7 @@ // // except that the closure does so via a second closure. -// compile-flags:-Znll -Zborrowck=mir -Zverbose +// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose #![feature(rustc_attrs)] diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index 277e5201416a..430fb711c635 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -58,6 +58,9 @@ error[E0597]: `y` does not live long enough ... 36 | } | - borrowed value only lives until here +37 | +38 | deref(p); + | - borrow later used here | = note: borrowed value must be valid for lifetime '_#4r... diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs index 93d8bfafcbaa..499ebd659556 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs @@ -19,7 +19,7 @@ // `'b`. This relationship is propagated to the closure creator, // which reports an error. -// compile-flags:-Znll -Zborrowck=mir -Zverbose +// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose #![feature(rustc_attrs)] diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index 709e6bc2de15..090bacbc17d0 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -35,6 +35,9 @@ error[E0597]: `y` does not live long enough ... 36 | } | - borrowed value only lives until here +37 | +38 | deref(p); + | - borrow later used here | = note: borrowed value must be valid for lifetime '_#4r... diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs index e5944e75e424..7c52a0c87af9 100644 --- a/src/test/ui/nll/get_default.rs +++ b/src/test/ui/nll/get_default.rs @@ -13,7 +13,7 @@ // a variety of errors from the older, AST-based machinery (notably // borrowck), and then we get the NLL error at the end. -// compile-flags:-Znll -Zborrowck=compare +// compile-flags:-Znll -Zborrowck=compare -Znll-dump-cause struct Map { } diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr index fff2684af139..ed2c305090cc 100644 --- a/src/test/ui/nll/get_default.stderr +++ b/src/test/ui/nll/get_default.stderr @@ -42,6 +42,9 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm 43 | Some(v) => { 44 | map.set(String::new()); // Both AST and MIR error here | ^^^ mutable borrow occurs here +... +47 | return v; + | - borrow later used here error: aborting due to 4 previous errors diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs index 0047f6d59237..184dfe320d33 100644 --- a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll -Znll-dump-cause + #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr index 389334f9c1d8..b161a068c040 100644 --- a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr @@ -6,6 +6,9 @@ error[E0506]: cannot assign to `x` because it is borrowed ... 31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] | ^^^^^ assignment to borrowed `x` occurs here +32 | // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones +33 | } + | - borrow later used here, when `foo` is dropped error: aborting due to previous error diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs index 3242136f005e..beb2c87f8f3b 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr index 9edeca2d1880..072818c7ce17 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr @@ -6,6 +6,8 @@ error[E0506]: cannot assign to `x` because it is borrowed ... 31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] | ^^^^^ assignment to borrowed `x` occurs here +32 | } + | - borrow later used here, when `foo` is dropped error: aborting due to previous error diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs index 3e32818b8dcf..39cad8acee18 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr index 24d0d6d04c8d..89117c2bfeaf 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr @@ -6,6 +6,9 @@ error[E0506]: cannot assign to `x` because it is borrowed ... 32 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] | ^^^^^ assignment to borrowed `x` occurs here +33 | // FIXME ^ This currently errors and it should not. +34 | } + | - borrow later used here, when `foo` is dropped error: aborting due to previous error diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs index 291fcbd73f3e..767c5b9b8be8 100644 --- a/src/test/ui/nll/maybe-initialized-drop.rs +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr index 7b1b55d133ac..626307a80ed5 100644 --- a/src/test/ui/nll/maybe-initialized-drop.stderr +++ b/src/test/ui/nll/maybe-initialized-drop.stderr @@ -5,6 +5,8 @@ error[E0506]: cannot assign to `x` because it is borrowed | ------ borrow of `x` occurs here 26 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] | ^^^^^ assignment to borrowed `x` occurs here +27 | } + | - borrow later used here, when `wrap` is dropped error: aborting due to previous error