Skip to content

Maybe initialized liveness #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,482 changes: 924 additions & 558 deletions src/librustc_mir/borrow_check.rs → src/librustc_mir/borrow_check/mod.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,54 @@ use rustc::hir;
use rustc::mir::{Location, Lvalue, Mir, Rvalue};
use rustc::mir::visit::Visitor;
use rustc::mir::Lvalue::Projection;
use rustc::mir::{LvalueProjection, ProjectionElem};
use rustc::mir::{LvalueProjection, ProjectionElem, Local};
use rustc::infer::InferCtxt;
use rustc::traits::{self, ObligationCause};
use rustc::ty::{self, Ty};
use rustc::ty::fold::TypeFoldable;
use rustc::util::common::ErrorReported;
use rustc_data_structures::fx::FxHashSet;
use syntax::codemap::DUMMY_SP;
use borrow_check::FlowInProgress;
use dataflow::MaybeInitializedLvals;
use dataflow::move_paths::{MoveData, HasMoveData};

use super::LivenessResults;
use super::ToRegionVid;
use super::region_infer::RegionInferenceContext;

pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &mut RegionInferenceContext<'tcx>,
mir: &Mir<'tcx>,
param_env: ty::ParamEnv<'tcx>,
liveness: &LivenessResults,
flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) {
ConstraintGeneration {
infcx,
regioncx,
mir,
liveness,
param_env,
flow_inits,
move_data,
}.add_constraints();
}

struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &'cx mut RegionInferenceContext<'tcx>,
mir: &'cx Mir<'tcx>,
liveness: &'cx LivenessResults,
/// 'cg = the duration of the constraint generation process itself.
struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &'cg mut RegionInferenceContext<'tcx>,
mir: &'cg Mir<'tcx>,
liveness: &'cg LivenessResults,
param_env: ty::ParamEnv<'tcx>,
flow_inits: &'cg mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
move_data: &'cg MoveData<'tcx>,
}

impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
fn add_constraints(&mut self) {
self.add_liveness_constraints();
self.add_borrow_constraints();
Expand All @@ -73,14 +83,51 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
}
});

self.liveness
.drop
.simulate_block(self.mir, bb, |location, live_locals| {
for live_local in live_locals.iter() {
let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
self.liveness.drop.simulate_block(self.mir, bb, |location, live_locals| {
all_live_locals.push((location, live_locals.iter().collect()));
});
debug!("add_liveness_constraints: all_live_locals={:#?}", all_live_locals);

let terminator_index = self.mir.basic_blocks()[bb].statements.len();
self.flow_inits.reset_to_entry_of(bb);
while let Some((location, live_locals)) = all_live_locals.pop() {
for live_local in live_locals {
debug!("add_liveness_constraints: location={:?} live_local={:?}", location,
live_local);

self.flow_inits.each_state_bit(|mpi_init| {
debug!("add_liveness_constraints: location={:?} initialized={:?}",
location,
&self.flow_inits
.base_results
.operator()
.move_data()
.move_paths[mpi_init]);
});

let mpi = self.move_data.rev_lookup.find_local(live_local);
if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
debug!("add_liveness_constraints: mpi={:?} has initialized child {:?}",
self.move_data.move_paths[mpi],
self.move_data.move_paths[initialized_child]);

let live_local_ty = self.mir.local_decls[live_local].ty;
self.add_drop_live_constraint(live_local_ty, location);
}
});
}

if location.statement_index == terminator_index {
debug!("add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
location);
self.flow_inits.reconstruct_terminator_effect(location);
} else {
debug!("add_liveness_constraints: reconstruct_statement_effect from {:#?}",
location);
self.flow_inits.reconstruct_statement_effect(location);
}
self.flow_inits.apply_local_effect();
}
}
}

Expand Down Expand Up @@ -212,7 +259,7 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
}
}

impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> {
fn visit_rvalue(&mut self,
rvalue: &Rvalue<'tcx>,
location: Location) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,62 @@ use std::collections::BTreeSet;
use transform::MirSource;
use transform::type_check;
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
use borrow_check::FlowInProgress;
use dataflow::MaybeInitializedLvals;
use dataflow::move_paths::MoveData;

use util as mir_util;
use self::mir_util::PassWhere;

mod constraint_generation;
mod subtype_constraint_generation;
mod free_regions;
use self::free_regions::FreeRegions;

pub(crate) mod region_infer;
use self::region_infer::RegionInferenceContext;

mod renumber;

/// Computes the (non-lexical) regions from the input MIR.
///
/// This may result in errors being reported.
pub fn compute_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
/// Rewrites the regions in the MIR to use NLL variables, also
/// scraping out the set of free regions (e.g., region parameters)
/// declared on the function. That set will need to be given to
/// `compute_regions`.
pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
def_id: DefId,
param_env: ty::ParamEnv<'gcx>,
mir: &mut Mir<'tcx>,
) -> RegionInferenceContext<'tcx> {
) -> FreeRegions<'tcx> {
// Compute named region information.
let free_regions = &free_regions::free_regions(infcx, def_id);
let free_regions = free_regions::free_regions(infcx, def_id);

// Replace all regions with fresh inference variables.
renumber::renumber_mir(infcx, free_regions, mir);
renumber::renumber_mir(infcx, &free_regions, mir);

free_regions
}

/// Computes the (non-lexical) regions from the input MIR.
///
/// This may result in errors being reported.
pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
def_id: DefId,
free_regions: FreeRegions<'tcx>,
mir: &Mir<'tcx>,
param_env: ty::ParamEnv<'gcx>,
flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) -> RegionInferenceContext<'tcx> {
// Run the MIR type-checker.
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir, def_id);

// Create the region inference context, taking ownership of the region inference
// data that was contained in `infcx`.
let var_origins = infcx.take_region_var_origins();
let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
subtype_constraint_generation::generate(&mut regioncx, free_regions, mir, constraint_sets);
let mut regioncx = RegionInferenceContext::new(var_origins, &free_regions, mir);
subtype_constraint_generation::generate(&mut regioncx, &free_regions, mir, constraint_sets);

// Compute what is live where.
let liveness = &LivenessResults {
Expand All @@ -75,7 +94,15 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
};

// Generate non-subtyping constraints.
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness);
constraint_generation::generate_constraints(
infcx,
&mut regioncx,
&mir,
param_env,
liveness,
flow_inits,
move_data,
);

// Solve the region constraints.
regioncx.solve(infcx, &mir);
Expand Down
10 changes: 5 additions & 5 deletions src/librustc_mir/dataflow/impls/borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use rustc_data_structures::indexed_vec::{IndexVec};

use dataflow::{BitDenotation, BlockSets, DataflowOperator};
pub use dataflow::indexes::BorrowIndex;
use transform::nll::region_infer::RegionInferenceContext;
use transform::nll::ToRegionVid;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;

use syntax_pos::Span;

Expand All @@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
region_span_map: FxHashMap<RegionKind, Span>,
nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>,
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>,
}

// temporarily allow some dead fields: `kind` and `region` will be
Expand Down Expand Up @@ -69,7 +69,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>)
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>)
-> Self {
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
location_map: FxHashMap(),
Expand Down Expand Up @@ -143,7 +143,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
fn kill_loans_out_of_scope_at_location(&self,
sets: &mut BlockSets<BorrowIndex>,
location: Location) {
if let Some(regioncx) = self.nonlexical_regioncx {
if let Some(ref regioncx) = self.nonlexical_regioncx {
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
let borrow_region = borrow_data.region.to_region_vid();
if !regioncx.region_contains_point(borrow_region, location) {
Expand Down
4 changes: 4 additions & 0 deletions src/librustc_mir/dataflow/move_paths/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ impl<'tcx> MovePathLookup<'tcx> {
}
}
}

pub fn find_local(&self, local: Local) -> MovePathIndex {
self.locals[local]
}
}

#[derive(Debug)]
Expand Down
1 change: 0 additions & 1 deletion src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ pub mod instcombine;
pub mod copy_prop;
pub mod generator;
pub mod inline;
pub mod nll;

pub(crate) fn provide(providers: &mut Providers) {
self::qualify_consts::provide(providers);
Expand Down
33 changes: 33 additions & 0 deletions src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//compile-flags: -Z emit-end-regions -Z borrowck-mir -Z nll

#![allow(warnings)]

struct Wrap<'p> { p: &'p mut i32 }

impl<'p> Drop for Wrap<'p> {
fn drop(&mut self) {
*self.p += 1;
}
}

struct Foo<'p> { a: String, b: Wrap<'p> }

fn main() {
let mut x = 0;
let wrap = Wrap { p: &mut x };
let s = String::from("str");
let foo = Foo { a: s, b: wrap };
std::mem::drop(foo.b);
x = 1; //~ ERROR because of Foo.a's implicit dtor
// FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0506]: cannot assign to `x` because it is borrowed (Ast)
--> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:31:5
|
27 | let wrap = Wrap { p: &mut x };
| - borrow of `x` occurs here
...
31 | x = 1; //~ ERROR because of Foo.a's implicit dtor
| ^^^^^ assignment to borrowed `x` occurs here

error[E0506]: cannot assign to `x` because it is borrowed (Mir)
--> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:31:5
|
27 | let wrap = Wrap { p: &mut x };
| ------ borrow of `x` occurs here
...
31 | x = 1; //~ ERROR because of Foo.a's implicit dtor
| ^^^^^ assignment to borrowed `x` occurs here

error: aborting due to 2 previous errors

28 changes: 28 additions & 0 deletions src/test/ui/nll/maybe-initialized-drop-uninitialized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//compile-flags: -Z emit-end-regions -Z borrowck-mir -Z nll

#![allow(warnings)]

struct Wrap<'p> { p: &'p mut i32 }

impl<'p> Drop for Wrap<'p> {
fn drop(&mut self) {
*self.p += 1;
}
}

fn main() {
let mut x = 0;
let wrap = Wrap { p: &mut x };
std::mem::drop(wrap);
x = 1; // OK, drop is inert
}
11 changes: 11 additions & 0 deletions src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0506]: cannot assign to `x` because it is borrowed (Ast)
--> $DIR/maybe-initialized-drop-uninitialized.rs:27:5
|
25 | let wrap = Wrap { p: &mut x };
| - borrow of `x` occurs here
26 | std::mem::drop(wrap);
27 | x = 1; // OK, drop is inert
| ^^^^^ assignment to borrowed `x` occurs here

error: aborting due to previous error

Loading