Skip to content

Commit

Permalink
Auto merge of #45538 - nikomatsakis:nll-liveness, r=pnkfelix
Browse files Browse the repository at this point in the history
enable non-lexical lifetimes in the MIR borrow checker

This PR, joint work with @spastorino, fills out the NLL infrastructure and integrates it with the borrow checker. **Don't get too excited:** it includes still a number of hacks (the subtyping code is particularly hacky). However, it *does* kinda' work. =)

The final commit demonstrates this by including a test that -- with both the AST borrowck and MIR borrowck -- reports an error by default. But if you pass `-Znll`, you only get an error from the AST borrowck, demonstrating that the integration succeeds:

```
struct MyStruct {
    field: String
}

fn main() {
    let mut my_struct = MyStruct { field: format!("Hello") };

    let value = &my_struct.field;
    if value.is_empty() {
        my_struct.field.push_str("Hello, world!");
        //~^ ERROR cannot borrow (Ast)
    }
}
```
  • Loading branch information
bors committed Nov 1, 2017
2 parents a3f990d + aae3e74 commit 2be4cc0
Show file tree
Hide file tree
Showing 43 changed files with 2,102 additions and 622 deletions.
2 changes: 1 addition & 1 deletion src/librustc/middle/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ pub struct BlockRemainder {

newtype_index!(FirstStatementIndex
{
DEBUG_NAME = "",
DEBUG_FORMAT = "{}",
MAX = SCOPE_DATA_REMAINDER_MAX,
});

Expand Down
18 changes: 13 additions & 5 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ pub enum BorrowKind {

newtype_index!(Local
{
DEBUG_NAME = "_",
DEBUG_FORMAT = "_{}",
const RETURN_POINTER = 0,
});

Expand Down Expand Up @@ -553,7 +553,7 @@ pub struct UpvarDecl {
///////////////////////////////////////////////////////////////////////////
// BasicBlock

newtype_index!(BasicBlock { DEBUG_NAME = "bb" });
newtype_index!(BasicBlock { DEBUG_FORMAT = "bb{}" });

///////////////////////////////////////////////////////////////////////////
// BasicBlockData and Terminator
Expand Down Expand Up @@ -1135,7 +1135,7 @@ pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Local, Ty<'tcx>
/// and the index is a local.
pub type LvalueElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>;

newtype_index!(Field { DEBUG_NAME = "field" });
newtype_index!(Field { DEBUG_FORMAT = "field[{}]" });

impl<'tcx> Lvalue<'tcx> {
pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> {
Expand Down Expand Up @@ -1202,7 +1202,7 @@ impl<'tcx> Debug for Lvalue<'tcx> {

newtype_index!(VisibilityScope
{
DEBUG_NAME = "scope",
DEBUG_FORMAT = "scope[{}]",
const ARGUMENT_VISIBILITY_SCOPE = 0,
});

Expand Down Expand Up @@ -1529,7 +1529,7 @@ pub struct Constant<'tcx> {
pub literal: Literal<'tcx>,
}

newtype_index!(Promoted { DEBUG_NAME = "promoted" });
newtype_index!(Promoted { DEBUG_FORMAT = "promoted[{}]" });

#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub enum Literal<'tcx> {
Expand Down Expand Up @@ -1637,6 +1637,14 @@ impl fmt::Debug for Location {
}

impl Location {
/// Returns the location immediately after this one within the enclosing block.
///
/// Note that if this location represents a terminator, then the
/// resulting location would be out of bounds and invalid.
pub fn successor_within_block(&self) -> Location {
Location { block: self.block, statement_index: self.statement_index + 1 }
}

pub fn dominates(&self, other: &Location, dominators: &Dominators<BasicBlock>) -> bool {
if self.block == other.block {
self.statement_index <= other.statement_index
Expand Down
6 changes: 3 additions & 3 deletions src/librustc/mir/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ pub enum MirSource {
GeneratorDrop(NodeId),
}

impl<'a, 'tcx> MirSource {
pub fn from_local_def_id(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> MirSource {
impl<'a, 'gcx, 'tcx> MirSource {
pub fn from_local_def_id(tcx: TyCtxt<'a, 'gcx, 'tcx>, def_id: DefId) -> MirSource {
let id = tcx.hir.as_local_node_id(def_id).expect("mir source requires local def-id");
Self::from_node(tcx, id)
}

pub fn from_node(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: NodeId) -> MirSource {
pub fn from_node(tcx: TyCtxt<'a, 'gcx, 'tcx>, id: NodeId) -> MirSource {
use hir::*;

// Handle constants in enum discriminants, types, and repeat expressions.
Expand Down
37 changes: 37 additions & 0 deletions src/librustc/ty/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,43 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
{
value.fold_with(&mut RegionFolder::new(self, skipped_regions, &mut f))
}

pub fn for_each_free_region<T,F>(self,
value: &T,
callback: F)
where F: FnMut(ty::Region<'tcx>),
T: TypeFoldable<'tcx>,
{
value.visit_with(&mut RegionVisitor { current_depth: 0, callback });

struct RegionVisitor<F> {
current_depth: u32,
callback: F,
}

impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor<F>
where F : FnMut(ty::Region<'tcx>)
{
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> bool {
self.current_depth += 1;
t.skip_binder().visit_with(self);
self.current_depth -= 1;

false // keep visiting
}

fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
match *r {
ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => {
/* ignore bound regions */
}
_ => (self.callback)(r),
}

false // keep visiting
}
}
}
}

/// Folds over the substructure of a type, visiting its component
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_borrowck/borrowck/check_loans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
// 3. Where does old loan expire.

let previous_end_span =
old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree).end_point();
Some(old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree)
.end_point());

let mut err = match (new_loan.kind, old_loan.kind) {
(ty::MutBorrow, ty::MutBorrow) =>
Expand Down
12 changes: 10 additions & 2 deletions src/librustc_data_structures/indexed_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,19 @@ pub struct IdxSet<T: Idx> {
}

impl<T: Idx> fmt::Debug for IdxSetBuf<T> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) }
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
w.debug_list()
.entries(self.iter())
.finish()
}
}

impl<T: Idx> fmt::Debug for IdxSet<T> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) }
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
w.debug_list()
.entries(self.iter())
.finish()
}
}

impl<T: Idx> IdxSetBuf<T> {
Expand Down
35 changes: 19 additions & 16 deletions src/librustc_data_structures/indexed_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,22 @@ macro_rules! newtype_index {
newtype_index!(
@type[$name]
@max[::std::u32::MAX]
@debug_name[unsafe {::std::intrinsics::type_name::<$name>() }]);
@debug_format["{}"]);
);

// Define any constants
($name:ident { $($tokens:tt)+ }) => (
newtype_index!(
@type[$name]
@max[::std::u32::MAX]
@debug_name[unsafe {::std::intrinsics::type_name::<$name>() }]
@debug_format["{}"]
$($tokens)+);
);

// ---- private rules ----

// Base case, user-defined constants (if any) have already been defined
(@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]) => (
(@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]) => (
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
RustcEncodable, RustcDecodable)]
pub struct $type(pub u32);
Expand All @@ -79,40 +79,43 @@ macro_rules! newtype_index {

impl ::std::fmt::Debug for $type {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(fmt, "{}{}", $debug_name, self.0)
write!(fmt, $debug_format, self.0)
}
}
);

// Rewrite final without comma to one that includes comma
(@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]
(@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]
$name:ident = $constant:expr) => (
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $name = $constant,);
newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $name = $constant,);
);

// Rewrite final const without comma to one that includes comma
(@type[$type:ident] @max[$_max:expr] @debug_name[$debug_name:expr]
(@type[$type:ident] @max[$_max:expr] @debug_format[$debug_format:expr]
const $name:ident = $constant:expr) => (
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] const $name = $constant,);
newtype_index!(@type[$type]
@max[$max]
@debug_format[$debug_format]
const $name = $constant,);
);

// Replace existing default for max
(@type[$type:ident] @max[$_max:expr] @debug_name[$debug_name:expr]
(@type[$type:ident] @max[$_max:expr] @debug_format[$debug_format:expr]
MAX = $max:expr, $($tokens:tt)*) => (
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
);

// Replace existing default for debug_name
(@type[$type:ident] @max[$max:expr] @debug_name[$_debug_name:expr]
DEBUG_NAME = $debug_name:expr, $($tokens:tt)*) => (
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
// Replace existing default for debug_format
(@type[$type:ident] @max[$max:expr] @debug_format[$_debug_format:expr]
DEBUG_FORMAT = $debug_format:expr, $($tokens:tt)*) => (
newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
);

// Assign a user-defined constant (as final param)
(@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]
(@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]
const $name:ident = $constant:expr, $($tokens:tt)*) => (
pub const $name: $type = $type($constant);
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
);
}

Expand Down
1 change: 0 additions & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,

passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts"));
passes.push_pass(MIR_VALIDATED, mir::transform::nll::NLL);

// borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED.

Expand Down
Loading

0 comments on commit 2be4cc0

Please sign in to comment.