Skip to content

Commit

Permalink
Include bounds from promoted constants in NLL
Browse files Browse the repository at this point in the history
Previously, a promoted that contains a function item wouldn't have the
function items bounds propagated to
the main function body.
  • Loading branch information
matthewjasper committed Mar 1, 2019
1 parent c1d2d83 commit 60eeed3
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 22 deletions.
4 changes: 2 additions & 2 deletions src/librustc_mir/borrow_check/nll/region_infer/values.rs
Expand Up @@ -154,10 +154,10 @@ impl<N: Idx> LivenessValues<N> {
/// Creates a new set of "region values" that tracks causal information.
/// Each of the regions in num_region_variables will be initialized with an
/// empty set of points and no causal information.
crate fn new(elements: &Rc<RegionValueElements>) -> Self {
crate fn new(elements: Rc<RegionValueElements>) -> Self {
Self {
elements: elements.clone(),
points: SparseBitMatrix::new(elements.num_points),
elements: elements,
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/librustc_mir/borrow_check/nll/renumber.rs
Expand Up @@ -47,6 +47,14 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
}

impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
fn visit_mir(&mut self, mir: &mut Mir<'tcx>) {
for promoted in mir.promoted.iter_mut() {
self.visit_mir(promoted);
}

self.super_mir(mir);
}

fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context);

Expand Down
114 changes: 97 additions & 17 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Expand Up @@ -45,7 +45,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use rustc::ty::layout::VariantIdx;
use std::rc::Rc;
use std::{fmt, iter};
use std::{fmt, iter, mem};
use syntax_pos::{Span, DUMMY_SP};

macro_rules! span_mirbug {
Expand Down Expand Up @@ -124,7 +124,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
let mut constraints = MirTypeckRegionConstraints {
placeholder_indices: PlaceholderIndices::default(),
placeholder_index_to_region: IndexVec::default(),
liveness_constraints: LivenessValues::new(elements),
liveness_constraints: LivenessValues::new(elements.clone()),
outlives_constraints: ConstraintSet::default(),
closure_bounds_mapping: Default::default(),
type_tests: Vec::default(),
Expand Down Expand Up @@ -253,7 +253,7 @@ enum FieldAccessError {
/// is a problem.
struct TypeVerifier<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> {
cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mir: &'b Mir<'tcx>,
last_span: Span,
mir_def_id: DefId,
errors_reported: bool,
Expand Down Expand Up @@ -385,7 +385,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
}

impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'b Mir<'tcx>) -> Self {
TypeVerifier {
mir,
mir_def_id: cx.mir_def_id,
Expand Down Expand Up @@ -454,19 +454,31 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
Place::Base(PlaceBase::Local(index)) => PlaceTy::Ty {
ty: self.mir.local_decls[index].ty,
},
Place::Base(PlaceBase::Promoted(box (_index, sty))) => {
Place::Base(PlaceBase::Promoted(box (index, sty))) => {
let sty = self.sanitize_type(place, sty);
// FIXME -- promoted MIR return types reference
// various "free regions" (e.g., scopes and things)
// that they ought not to do. We have to figure out
// how best to handle that -- probably we want treat
// promoted MIR much like closures, renumbering all
// their free regions and propagating constraints
// upwards. We have the same acyclic guarantees, so
// that should be possible. But for now, ignore them.
//
// let promoted_mir = &self.mir.promoted[index];
// promoted_mir.return_ty()

if !self.errors_reported {
let promoted_mir = &self.mir.promoted[index];
self.sanitize_promoted(promoted_mir, location);

let promoted_ty = promoted_mir.return_ty();

if let Err(terr) = self.cx.eq_types(
sty,
promoted_ty,
location.to_locations(),
ConstraintCategory::Boring,
) {
span_mirbug!(
self,
place,
"bad promoted type ({:?}: {:?}): {:?}",
promoted_ty,
sty,
terr
);
};
}
PlaceTy::Ty { ty: sty }
}
Place::Base(PlaceBase::Static(box Static { def_id, ty: sty })) => {
Expand Down Expand Up @@ -533,6 +545,74 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
place_ty
}

fn sanitize_promoted(&mut self, promoted_mir: &'b Mir<'tcx>, location: Location) {
// Determine the constraints from the promoted MIR by running the type
// checker on the promoted MIR, then transfer the constraints back to
// the main MIR, changing the locations to the provided location.

let main_mir = mem::replace(&mut self.mir, promoted_mir);
self.cx.mir = promoted_mir;

let all_facts = &mut None;
let mut constraints = Default::default();
let mut closure_bounds = Default::default();
if let Some(ref mut bcx) = self.cx.borrowck_context {
// Don't try to add borrow_region facts for the promoted MIR
mem::swap(bcx.all_facts, all_facts);

// Use a new sets of constraints and closure bounds so that we can
// modify their locations.
mem::swap(&mut bcx.constraints.outlives_constraints, &mut constraints);
mem::swap(&mut bcx.constraints.closure_bounds_mapping, &mut closure_bounds);
};

self.visit_mir(promoted_mir);

if !self.errors_reported {
// if verifier failed, don't do further checks to avoid ICEs
self.cx.typeck_mir(promoted_mir);
}

self.mir = main_mir;
self.cx.mir = main_mir;
// Merge the outlives constraints back in, at the given location.
if let Some(ref mut base_bcx) = self.cx.borrowck_context {
mem::swap(base_bcx.all_facts, all_facts);
mem::swap(&mut base_bcx.constraints.outlives_constraints, &mut constraints);
mem::swap(&mut base_bcx.constraints.closure_bounds_mapping, &mut closure_bounds);

let locations = location.to_locations();
for constraint in constraints.iter() {
let mut constraint = *constraint;
constraint.locations = locations;
if let ConstraintCategory::Return
| ConstraintCategory::UseAsConst
| ConstraintCategory::UseAsStatic = constraint.category
{
// "Returning" from a promoted is an assigment to a
// temporary from the user's point of view.
constraint.category = ConstraintCategory::Boring;
}
base_bcx.constraints.outlives_constraints.push(constraint)
}

if !closure_bounds.is_empty() {
let combined_bounds_mapping = closure_bounds
.into_iter()
.flat_map(|(_, value)| value)
.collect();
let existing = base_bcx
.constraints
.closure_bounds_mapping
.insert(location, combined_bounds_mapping);
assert!(
existing.is_none(),
"Multiple promoteds/closures at the same location."
);
}
}
}

fn sanitize_projection(
&mut self,
base: PlaceTy<'tcx>,
Expand Down Expand Up @@ -2275,7 +2355,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
) -> ty::InstantiatedPredicates<'tcx> {
if let Some(closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements {
let closure_constraints =
closure_region_requirements.apply_requirements(tcx, location, def_id, substs);
closure_region_requirements.apply_requirements(tcx, def_id, substs);

if let Some(ref mut borrowck_context) = self.borrowck_context {
let bounds_mapping = closure_constraints
Expand Down
4 changes: 1 addition & 3 deletions src/test/ui/nll/issue-48697.rs
@@ -1,14 +1,12 @@
// Regression test for #48697

// compile-pass

#![feature(nll)]

fn foo(x: &i32) -> &i32 {
let z = 4;
let f = &|y| y;
let k = f(&z);
f(x)
f(x) //~ cannot return value referencing local variable
}

fn main() {}
11 changes: 11 additions & 0 deletions src/test/ui/nll/issue-48697.stderr
@@ -0,0 +1,11 @@
error[E0515]: cannot return value referencing local variable `z`
--> $DIR/issue-48697.rs:9:5
|
LL | let k = f(&z);
| -- `z` is borrowed here
LL | f(x) //~ cannot return value referencing local variable
| ^^^^ returns a value referencing data owned by the current function

error: aborting due to previous error

For more information about this error, try `rustc --explain E0515`.
27 changes: 27 additions & 0 deletions src/test/ui/nll/promoted-bounds.rs
@@ -0,0 +1,27 @@
#![feature(nll)]

fn shorten_lifetime<'a, 'b, 'min>(a: &'a i32, b: &'b i32) -> &'min i32
where
'a: 'min,
'b: 'min,
{
if *a < *b {
&a
} else {
&b
}
}

fn main() {
let promoted_fn_item_ref = &shorten_lifetime;

let a = &5;
let ptr = {
let l = 3;
let b = &l; //~ ERROR does not live long enough
let c = promoted_fn_item_ref(a, b);
c
};

println!("ptr = {:?}", ptr);
}
15 changes: 15 additions & 0 deletions src/test/ui/nll/promoted-bounds.stderr
@@ -0,0 +1,15 @@
error[E0597]: `l` does not live long enough
--> $DIR/promoted-bounds.rs:21:17
|
LL | let ptr = {
| --- borrow later stored here
LL | let l = 3;
LL | let b = &l; //~ ERROR does not live long enough
| ^^ borrowed value does not live long enough
...
LL | };
| - `l` dropped here while still borrowed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
12 changes: 12 additions & 0 deletions src/test/ui/nll/promoted-closure-pair.rs
@@ -0,0 +1,12 @@
// Check that we handle multiple closures in the same promoted constant.

#![feature(nll)]

fn foo() -> &'static i32 {
let z = 0;
let p = &(|y| y, |y| y);
p.0(&z);
p.1(&z) //~ ERROR cannot return
}

fn main() {}
12 changes: 12 additions & 0 deletions src/test/ui/nll/promoted-closure-pair.stderr
@@ -0,0 +1,12 @@
error[E0515]: cannot return value referencing local variable `z`
--> $DIR/promoted-closure-pair.rs:9:5
|
LL | p.1(&z) //~ ERROR cannot return
| ^^^^--^
| | |
| | `z` is borrowed here
| returns a value referencing data owned by the current function

error: aborting due to previous error

For more information about this error, try `rustc --explain E0515`.

0 comments on commit 60eeed3

Please sign in to comment.