Skip to content

Commit a21d824

Browse files
committed
Auto merge of #57202 - matthewjasper:nll-typeck-promoteds, r=pnkfelix
Include bounds from promoted constants in NLL Previously a promoted function wouldn't have its bound propagated out to the main function body. When we visit a promoted, we now type check the MIR of the promoted and transfer any lifetime constraints to back to the main function's MIR. Fixes #57170 r? @nikomatsakis
2 parents d987b46 + 3b93d71 commit a21d824

File tree

12 files changed

+229
-37
lines changed

12 files changed

+229
-37
lines changed

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -1357,7 +1357,6 @@ pub trait ClosureRegionRequirementsExt<'gcx, 'tcx> {
13571357
fn apply_requirements(
13581358
&self,
13591359
tcx: TyCtxt<'_, 'gcx, 'tcx>,
1360-
location: Location,
13611360
closure_def_id: DefId,
13621361
closure_substs: SubstsRef<'tcx>,
13631362
) -> Vec<QueryRegionConstraint<'tcx>>;
@@ -1388,13 +1387,12 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi
13881387
fn apply_requirements(
13891388
&self,
13901389
tcx: TyCtxt<'_, 'gcx, 'tcx>,
1391-
location: Location,
13921390
closure_def_id: DefId,
13931391
closure_substs: SubstsRef<'tcx>,
13941392
) -> Vec<QueryRegionConstraint<'tcx>> {
13951393
debug!(
1396-
"apply_requirements(location={:?}, closure_def_id={:?}, closure_substs={:?})",
1397-
location, closure_def_id, closure_substs
1394+
"apply_requirements(closure_def_id={:?}, closure_substs={:?})",
1395+
closure_def_id, closure_substs
13981396
);
13991397

14001398
// Extract the values of the free regions in `closure_substs`

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

+110-28
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ use rustc::ty::fold::TypeFoldable;
3939
use rustc::ty::subst::{Subst, SubstsRef, UnpackedKind, UserSubsts};
4040
use rustc::ty::{
4141
self, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind, UserType,
42-
CanonicalUserTypeAnnotation, UserTypeAnnotationIndex,
42+
CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
43+
UserTypeAnnotationIndex,
4344
};
4445
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
4546
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
4647
use rustc::ty::layout::VariantIdx;
4748
use std::rc::Rc;
48-
use std::{fmt, iter};
49+
use std::{fmt, iter, mem};
4950
use syntax_pos::{Span, DUMMY_SP};
5051

5152
macro_rules! span_mirbug {
@@ -124,7 +125,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
124125
let mut constraints = MirTypeckRegionConstraints {
125126
placeholder_indices: PlaceholderIndices::default(),
126127
placeholder_index_to_region: IndexVec::default(),
127-
liveness_constraints: LivenessValues::new(elements),
128+
liveness_constraints: LivenessValues::new(elements.clone()),
128129
outlives_constraints: ConstraintSet::default(),
129130
closure_bounds_mapping: Default::default(),
130131
type_tests: Vec::default(),
@@ -253,7 +254,7 @@ enum FieldAccessError {
253254
/// is a problem.
254255
struct TypeVerifier<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> {
255256
cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
256-
mir: &'a Mir<'tcx>,
257+
mir: &'b Mir<'tcx>,
257258
last_span: Span,
258259
mir_def_id: DefId,
259260
errors_reported: bool,
@@ -283,7 +284,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
283284
location.to_locations(),
284285
ConstraintCategory::Boring,
285286
) {
286-
let annotation = &self.mir.user_type_annotations[annotation_index];
287+
let annotation = &self.cx.user_type_annotations[annotation_index];
287288
span_mirbug!(
288289
self,
289290
constant,
@@ -385,7 +386,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
385386
}
386387

387388
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 {
389+
fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'b Mir<'tcx>) -> Self {
389390
TypeVerifier {
390391
mir,
391392
mir_def_id: cx.mir_def_id,
@@ -454,19 +455,31 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
454455
Place::Base(PlaceBase::Local(index)) => PlaceTy::Ty {
455456
ty: self.mir.local_decls[index].ty,
456457
},
457-
Place::Base(PlaceBase::Promoted(box (_index, sty))) => {
458+
Place::Base(PlaceBase::Promoted(box (index, sty))) => {
458459
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()
460+
461+
if !self.errors_reported {
462+
let promoted_mir = &self.mir.promoted[index];
463+
self.sanitize_promoted(promoted_mir, location);
464+
465+
let promoted_ty = promoted_mir.return_ty();
466+
467+
if let Err(terr) = self.cx.eq_types(
468+
sty,
469+
promoted_ty,
470+
location.to_locations(),
471+
ConstraintCategory::Boring,
472+
) {
473+
span_mirbug!(
474+
self,
475+
place,
476+
"bad promoted type ({:?}: {:?}): {:?}",
477+
promoted_ty,
478+
sty,
479+
terr
480+
);
481+
};
482+
}
470483
PlaceTy::Ty { ty: sty }
471484
}
472485
Place::Base(PlaceBase::Static(box Static { def_id, ty: sty })) => {
@@ -533,6 +546,72 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
533546
place_ty
534547
}
535548

549+
fn sanitize_promoted(&mut self, promoted_mir: &'b Mir<'tcx>, location: Location) {
550+
// Determine the constraints from the promoted MIR by running the type
551+
// checker on the promoted MIR, then transfer the constraints back to
552+
// the main MIR, changing the locations to the provided location.
553+
554+
let parent_mir = mem::replace(&mut self.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 = parent_mir;
577+
// Merge the outlives constraints back in, at the given location.
578+
if let Some(ref mut base_bcx) = self.cx.borrowck_context {
579+
mem::swap(base_bcx.all_facts, all_facts);
580+
mem::swap(&mut base_bcx.constraints.outlives_constraints, &mut constraints);
581+
mem::swap(&mut base_bcx.constraints.closure_bounds_mapping, &mut closure_bounds);
582+
583+
let locations = location.to_locations();
584+
for constraint in constraints.iter() {
585+
let mut constraint = *constraint;
586+
constraint.locations = locations;
587+
if let ConstraintCategory::Return
588+
| ConstraintCategory::UseAsConst
589+
| ConstraintCategory::UseAsStatic = constraint.category
590+
{
591+
// "Returning" from a promoted is an assigment to a
592+
// temporary from the user's point of view.
593+
constraint.category = ConstraintCategory::Boring;
594+
}
595+
base_bcx.constraints.outlives_constraints.push(constraint)
596+
}
597+
598+
if !closure_bounds.is_empty() {
599+
let combined_bounds_mapping = closure_bounds
600+
.into_iter()
601+
.flat_map(|(_, value)| value)
602+
.collect();
603+
let existing = base_bcx
604+
.constraints
605+
.closure_bounds_mapping
606+
.insert(location, combined_bounds_mapping);
607+
assert!(
608+
existing.is_none(),
609+
"Multiple promoteds/closures at the same location."
610+
);
611+
}
612+
}
613+
}
614+
536615
fn sanitize_projection(
537616
&mut self,
538617
base: PlaceTy<'tcx>,
@@ -738,7 +817,9 @@ struct TypeChecker<'a, 'gcx: 'tcx, 'tcx: 'a> {
738817
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
739818
param_env: ty::ParamEnv<'gcx>,
740819
last_span: Span,
741-
mir: &'a Mir<'tcx>,
820+
/// User type annotations are shared between the main MIR and the MIR of
821+
/// all of the promoted items.
822+
user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>,
742823
mir_def_id: DefId,
743824
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
744825
implicit_region_bound: Option<ty::Region<'tcx>>,
@@ -893,8 +974,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
893974
let mut checker = Self {
894975
infcx,
895976
last_span: DUMMY_SP,
896-
mir,
897977
mir_def_id,
978+
user_type_annotations: &mir.user_type_annotations,
898979
param_env,
899980
region_bound_pairs,
900981
implicit_region_bound,
@@ -910,9 +991,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
910991
fn check_user_type_annotations(&mut self) {
911992
debug!(
912993
"check_user_type_annotations: user_type_annotations={:?}",
913-
self.mir.user_type_annotations
994+
self.user_type_annotations
914995
);
915-
for user_annotation in &self.mir.user_type_annotations {
996+
for user_annotation in self.user_type_annotations {
916997
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
917998
let (annotation, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars(
918999
span, user_ty
@@ -1095,7 +1176,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
10951176
a, v, user_ty, locations,
10961177
);
10971178

1098-
let annotated_type = self.mir.user_type_annotations[user_ty.base].inferred_ty;
1179+
let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty;
10991180
let mut curr_projected_ty = PlaceTy::from_ty(annotated_type);
11001181

11011182
let tcx = self.infcx.tcx;
@@ -1281,7 +1362,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
12811362
location.to_locations(),
12821363
ConstraintCategory::Boring,
12831364
) {
1284-
let annotation = &mir.user_type_annotations[annotation_index];
1365+
let annotation = &self.user_type_annotations[annotation_index];
12851366
span_mirbug!(
12861367
self,
12871368
stmt,
@@ -1340,7 +1421,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
13401421
Locations::All(stmt.source_info.span),
13411422
ConstraintCategory::TypeAnnotation,
13421423
) {
1343-
let annotation = &mir.user_type_annotations[projection.base];
1424+
let annotation = &self.user_type_annotations[projection.base];
13441425
span_mirbug!(
13451426
self,
13461427
stmt,
@@ -1998,7 +2079,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
19982079
}
19992080

20002081
Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
2001-
self.add_reborrow_constraint(location, region, borrowed_place);
2082+
self.add_reborrow_constraint(mir, location, region, borrowed_place);
20022083
}
20032084

20042085
// FIXME: These other cases have to be implemented in future PRs
@@ -2097,6 +2178,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
20972178
/// - `borrowed_place`: the place `P` being borrowed
20982179
fn add_reborrow_constraint(
20992180
&mut self,
2181+
mir: &Mir<'tcx>,
21002182
location: Location,
21012183
borrow_region: ty::Region<'tcx>,
21022184
borrowed_place: &Place<'tcx>,
@@ -2146,7 +2228,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
21462228
match *elem {
21472229
ProjectionElem::Deref => {
21482230
let tcx = self.infcx.tcx;
2149-
let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
2231+
let base_ty = base.ty(mir, tcx).to_ty(tcx);
21502232

21512233
debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
21522234
match base_ty.sty {
@@ -2275,7 +2357,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
22752357
) -> ty::InstantiatedPredicates<'tcx> {
22762358
if let Some(closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements {
22772359
let closure_constraints =
2278-
closure_region_requirements.apply_requirements(tcx, location, def_id, substs);
2360+
closure_region_requirements.apply_requirements(tcx, def_id, substs);
22792361

22802362
if let Some(ref mut borrowck_context) = self.borrowck_context {
22812363
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`.

0 commit comments

Comments
 (0)