Include bounds from promoted constants in NLL
Previously, a promoted that contains a function item wouldn't have the function items bounds propagated to the main function body.
This commit is contained in:
parent
c1d2d83ca3
commit
60eeed34af
9 changed files with 185 additions and 22 deletions
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -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(),
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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 })) => {
|
||||
|
|
@ -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>,
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
src/test/ui/nll/issue-48697.stderr
Normal file
11
src/test/ui/nll/issue-48697.stderr
Normal file
|
|
@ -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
src/test/ui/nll/promoted-bounds.rs
Normal file
27
src/test/ui/nll/promoted-bounds.rs
Normal file
|
|
@ -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
src/test/ui/nll/promoted-bounds.stderr
Normal file
15
src/test/ui/nll/promoted-bounds.stderr
Normal file
|
|
@ -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
src/test/ui/nll/promoted-closure-pair.rs
Normal file
12
src/test/ui/nll/promoted-closure-pair.rs
Normal file
|
|
@ -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
src/test/ui/nll/promoted-closure-pair.stderr
Normal file
12
src/test/ui/nll/promoted-closure-pair.stderr
Normal file
|
|
@ -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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue