Skip to content

Commit 60eeed3

Browse files
committed
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.
1 parent c1d2d83 commit 60eeed3

File tree

9 files changed

+185
-22
lines changed

9 files changed

+185
-22
lines changed

src/librustc_mir/borrow_check/nll/region_infer/values.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ impl<N: Idx> LivenessValues<N> {
154154
/// Creates a new set of "region values" that tracks causal information.
155155
/// Each of the regions in num_region_variables will be initialized with an
156156
/// empty set of points and no causal information.
157-
crate fn new(elements: &Rc<RegionValueElements>) -> Self {
157+
crate fn new(elements: Rc<RegionValueElements>) -> Self {
158158
Self {
159-
elements: elements.clone(),
160159
points: SparseBitMatrix::new(elements.num_points),
160+
elements: elements,
161161
}
162162
}
163163

src/librustc_mir/borrow_check/nll/renumber.rs

+8
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
4747
}
4848

4949
impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
50+
fn visit_mir(&mut self, mir: &mut Mir<'tcx>) {
51+
for promoted in mir.promoted.iter_mut() {
52+
self.visit_mir(promoted);
53+
}
54+
55+
self.super_mir(mir);
56+
}
57+
5058
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
5159
debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context);
5260

src/librustc_mir/borrow_check/nll/type_check/mod.rs

+97-17
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
4545
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
4646
use rustc::ty::layout::VariantIdx;
4747
use std::rc::Rc;
48-
use std::{fmt, iter};
48+
use std::{fmt, iter, mem};
4949
use syntax_pos::{Span, DUMMY_SP};
5050

5151
macro_rules! span_mirbug {
@@ -124,7 +124,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
124124
let mut constraints = MirTypeckRegionConstraints {
125125
placeholder_indices: PlaceholderIndices::default(),
126126
placeholder_index_to_region: IndexVec::default(),
127-
liveness_constraints: LivenessValues::new(elements),
127+
liveness_constraints: LivenessValues::new(elements.clone()),
128128
outlives_constraints: ConstraintSet::default(),
129129
closure_bounds_mapping: Default::default(),
130130
type_tests: Vec::default(),
@@ -253,7 +253,7 @@ enum FieldAccessError {
253253
/// is a problem.
254254
struct TypeVerifier<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> {
255255
cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
256-
mir: &'a Mir<'tcx>,
256+
mir: &'b Mir<'tcx>,
257257
last_span: Span,
258258
mir_def_id: DefId,
259259
errors_reported: bool,
@@ -385,7 +385,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
385385
}
386386

387387
impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
388-
fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
388+
fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'b Mir<'tcx>) -> Self {
389389
TypeVerifier {
390390
mir,
391391
mir_def_id: cx.mir_def_id,
@@ -454,19 +454,31 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
454454
Place::Base(PlaceBase::Local(index)) => PlaceTy::Ty {
455455
ty: self.mir.local_decls[index].ty,
456456
},
457-
Place::Base(PlaceBase::Promoted(box (_index, sty))) => {
457+
Place::Base(PlaceBase::Promoted(box (index, sty))) => {
458458
let sty = self.sanitize_type(place, sty);
459-
// FIXME -- promoted MIR return types reference
460-
// various "free regions" (e.g., scopes and things)
461-
// that they ought not to do. We have to figure out
462-
// how best to handle that -- probably we want treat
463-
// promoted MIR much like closures, renumbering all
464-
// their free regions and propagating constraints
465-
// upwards. We have the same acyclic guarantees, so
466-
// that should be possible. But for now, ignore them.
467-
//
468-
// let promoted_mir = &self.mir.promoted[index];
469-
// promoted_mir.return_ty()
459+
460+
if !self.errors_reported {
461+
let promoted_mir = &self.mir.promoted[index];
462+
self.sanitize_promoted(promoted_mir, location);
463+
464+
let promoted_ty = promoted_mir.return_ty();
465+
466+
if let Err(terr) = self.cx.eq_types(
467+
sty,
468+
promoted_ty,
469+
location.to_locations(),
470+
ConstraintCategory::Boring,
471+
) {
472+
span_mirbug!(
473+
self,
474+
place,
475+
"bad promoted type ({:?}: {:?}): {:?}",
476+
promoted_ty,
477+
sty,
478+
terr
479+
);
480+
};
481+
}
470482
PlaceTy::Ty { ty: sty }
471483
}
472484
Place::Base(PlaceBase::Static(box Static { def_id, ty: sty })) => {
@@ -533,6 +545,74 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
533545
place_ty
534546
}
535547

548+
fn sanitize_promoted(&mut self, promoted_mir: &'b Mir<'tcx>, location: Location) {
549+
// Determine the constraints from the promoted MIR by running the type
550+
// checker on the promoted MIR, then transfer the constraints back to
551+
// the main MIR, changing the locations to the provided location.
552+
553+
let main_mir = mem::replace(&mut self.mir, promoted_mir);
554+
self.cx.mir = promoted_mir;
555+
556+
let all_facts = &mut None;
557+
let mut constraints = Default::default();
558+
let mut closure_bounds = Default::default();
559+
if let Some(ref mut bcx) = self.cx.borrowck_context {
560+
// Don't try to add borrow_region facts for the promoted MIR
561+
mem::swap(bcx.all_facts, all_facts);
562+
563+
// Use a new sets of constraints and closure bounds so that we can
564+
// modify their locations.
565+
mem::swap(&mut bcx.constraints.outlives_constraints, &mut constraints);
566+
mem::swap(&mut bcx.constraints.closure_bounds_mapping, &mut closure_bounds);
567+
};
568+
569+
self.visit_mir(promoted_mir);
570+
571+
if !self.errors_reported {
572+
// if verifier failed, don't do further checks to avoid ICEs
573+
self.cx.typeck_mir(promoted_mir);
574+
}
575+
576+
self.mir = main_mir;
577+
self.cx.mir = main_mir;
578+
// Merge the outlives constraints back in, at the given location.
579+
if let Some(ref mut base_bcx) = self.cx.borrowck_context {
580+
mem::swap(base_bcx.all_facts, all_facts);
581+
mem::swap(&mut base_bcx.constraints.outlives_constraints, &mut constraints);
582+
mem::swap(&mut base_bcx.constraints.closure_bounds_mapping, &mut closure_bounds);
583+
584+
let locations = location.to_locations();
585+
for constraint in constraints.iter() {
586+
let mut constraint = *constraint;
587+
constraint.locations = locations;
588+
if let ConstraintCategory::Return
589+
| ConstraintCategory::UseAsConst
590+
| ConstraintCategory::UseAsStatic = constraint.category
591+
{
592+
// "Returning" from a promoted is an assigment to a
593+
// temporary from the user's point of view.
594+
constraint.category = ConstraintCategory::Boring;
595+
}
596+
base_bcx.constraints.outlives_constraints.push(constraint)
597+
}
598+
599+
if !closure_bounds.is_empty() {
600+
let combined_bounds_mapping = closure_bounds
601+
.into_iter()
602+
.flat_map(|(_, value)| value)
603+
.collect();
604+
let existing = base_bcx
605+
.constraints
606+
.closure_bounds_mapping
607+
.insert(location, combined_bounds_mapping);
608+
assert!(
609+
existing.is_none(),
610+
"Multiple promoteds/closures at the same location."
611+
);
612+
}
613+
}
614+
}
615+
536616
fn sanitize_projection(
537617
&mut self,
538618
base: PlaceTy<'tcx>,
@@ -2275,7 +2355,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
22752355
) -> ty::InstantiatedPredicates<'tcx> {
22762356
if let Some(closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements {
22772357
let closure_constraints =
2278-
closure_region_requirements.apply_requirements(tcx, location, def_id, substs);
2358+
closure_region_requirements.apply_requirements(tcx, def_id, substs);
22792359

22802360
if let Some(ref mut borrowck_context) = self.borrowck_context {
22812361
let bounds_mapping = closure_constraints

src/test/ui/nll/issue-48697.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
// Regression test for #48697
22

3-
// compile-pass
4-
53
#![feature(nll)]
64

75
fn foo(x: &i32) -> &i32 {
86
let z = 4;
97
let f = &|y| y;
108
let k = f(&z);
11-
f(x)
9+
f(x) //~ cannot return value referencing local variable
1210
}
1311

1412
fn main() {}

src/test/ui/nll/issue-48697.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0515]: cannot return value referencing local variable `z`
2+
--> $DIR/issue-48697.rs:9:5
3+
|
4+
LL | let k = f(&z);
5+
| -- `z` is borrowed here
6+
LL | f(x) //~ cannot return value referencing local variable
7+
| ^^^^ returns a value referencing data owned by the current function
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0515`.

src/test/ui/nll/promoted-bounds.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![feature(nll)]
2+
3+
fn shorten_lifetime<'a, 'b, 'min>(a: &'a i32, b: &'b i32) -> &'min i32
4+
where
5+
'a: 'min,
6+
'b: 'min,
7+
{
8+
if *a < *b {
9+
&a
10+
} else {
11+
&b
12+
}
13+
}
14+
15+
fn main() {
16+
let promoted_fn_item_ref = &shorten_lifetime;
17+
18+
let a = &5;
19+
let ptr = {
20+
let l = 3;
21+
let b = &l; //~ ERROR does not live long enough
22+
let c = promoted_fn_item_ref(a, b);
23+
c
24+
};
25+
26+
println!("ptr = {:?}", ptr);
27+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0597]: `l` does not live long enough
2+
--> $DIR/promoted-bounds.rs:21:17
3+
|
4+
LL | let ptr = {
5+
| --- borrow later stored here
6+
LL | let l = 3;
7+
LL | let b = &l; //~ ERROR does not live long enough
8+
| ^^ borrowed value does not live long enough
9+
...
10+
LL | };
11+
| - `l` dropped here while still borrowed
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0597`.
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Check that we handle multiple closures in the same promoted constant.
2+
3+
#![feature(nll)]
4+
5+
fn foo() -> &'static i32 {
6+
let z = 0;
7+
let p = &(|y| y, |y| y);
8+
p.0(&z);
9+
p.1(&z) //~ ERROR cannot return
10+
}
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0515]: cannot return value referencing local variable `z`
2+
--> $DIR/promoted-closure-pair.rs:9:5
3+
|
4+
LL | p.1(&z) //~ ERROR cannot return
5+
| ^^^^--^
6+
| | |
7+
| | `z` is borrowed here
8+
| returns a value referencing data owned by the current function
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0515`.

0 commit comments

Comments
 (0)