Skip to content

Commit ab1c1bc

Browse files
committed
mir-borrowck returns closure requirements, mir-typeck enforces
1 parent 2ec959f commit ab1c1bc

File tree

14 files changed

+1121
-184
lines changed

14 files changed

+1121
-184
lines changed

src/librustc/ich/impls_mir.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,3 +572,15 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Literal<'gcx> {
572572
}
573573

574574
impl_stable_hash_for!(struct mir::Location { block, statement_index });
575+
576+
impl_stable_hash_for!(struct mir::ClosureRegionRequirements {
577+
num_external_vids,
578+
outlives_requirements
579+
});
580+
581+
impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement {
582+
free_region,
583+
outlived_free_region,
584+
blame_span
585+
});
586+

src/librustc/ich/impls_ty.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ for ty::RegionKind {
8484
}
8585
}
8686

87+
impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::RegionVid {
88+
#[inline]
89+
fn hash_stable<W: StableHasherResult>(&self,
90+
hcx: &mut StableHashingContext<'gcx>,
91+
hasher: &mut StableHasher<W>) {
92+
use rustc_data_structures::indexed_vec::Idx;
93+
self.index().hash_stable(hcx, hasher);
94+
}
95+
}
96+
8797
impl<'gcx> HashStable<StableHashingContext<'gcx>>
8898
for ty::adjustment::AutoBorrow<'gcx> {
8999
fn hash_stable<W: StableHasherResult>(&self,

src/librustc/infer/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10621062
self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin)))
10631063
}
10641064

1065+
/// Number of region variables created so far.
1066+
pub fn num_region_vars(&self) -> usize {
1067+
self.borrow_region_constraints().var_origins().len()
1068+
}
1069+
10651070
/// Just a convenient wrapper of `next_region_var` for using during NLL.
10661071
pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin)
10671072
-> ty::Region<'tcx> {

src/librustc/mir/mod.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,6 +1789,75 @@ pub struct GeneratorLayout<'tcx> {
17891789
pub fields: Vec<LocalDecl<'tcx>>,
17901790
}
17911791

1792+
/// After we borrow check a closure, we are left with various
1793+
/// requirements that we have inferred between the free regions that
1794+
/// appear in the closure's signature or on its field types. These
1795+
/// requirements are then verified and proved by the closure's
1796+
/// creating function. This struct encodes those requirements.
1797+
///
1798+
/// The requirements are listed as being between various
1799+
/// `RegionVid`. The 0th region refers to `'static`; subsequent region
1800+
/// vids refer to the free regions that appear in the closure (or
1801+
/// generator's) type, in order of appearance. (This numbering is
1802+
/// actually defined by the `UniversalRegions` struct in the NLL
1803+
/// region checker. See for example
1804+
/// `UniversalRegions::closure_mapping`.) Note that we treat the free
1805+
/// regions in the closure's type "as if" they were erased, so their
1806+
/// precise identity is not important, only their position.
1807+
///
1808+
/// Example: If type check produces a closure with the closure substs:
1809+
///
1810+
/// ```
1811+
/// ClosureSubsts = [
1812+
/// i8, // the "closure kind"
1813+
/// for<'x> fn(&'a &'x u32) -> &'x u32, // the "closure signature"
1814+
/// &'a String, // some upvar
1815+
/// ]
1816+
/// ```
1817+
///
1818+
/// here, there is one unique free region (`'a`) but it appears
1819+
/// twice. We would "renumber" each occurence to a unique vid, as follows:
1820+
///
1821+
/// ```
1822+
/// ClosureSubsts = [
1823+
/// i8, // the "closure kind"
1824+
/// for<'x> fn(&'1 &'x u32) -> &'x u32, // the "closure signature"
1825+
/// &'2 String, // some upvar
1826+
/// ]
1827+
/// ```
1828+
///
1829+
/// Now the code might impose a requirement like `'1: '2`. When an
1830+
/// instance of the closure is created, the corresponding free regions
1831+
/// can be extracted from its type and constrained to have the given
1832+
/// outlives relationship.
1833+
#[derive(Clone, Debug)]
1834+
pub struct ClosureRegionRequirements {
1835+
/// The number of external regions defined on the closure. In our
1836+
/// example above, it would be 3 -- one for `'static`, then `'1`
1837+
/// and `'2`. This is just used for a sanity check later on, to
1838+
/// make sure that the number of regions we see at the callsite
1839+
/// matches.
1840+
pub num_external_vids: usize,
1841+
1842+
/// Requirements between the various free regions defined in
1843+
/// indices.
1844+
pub outlives_requirements: Vec<ClosureOutlivesRequirement>,
1845+
}
1846+
1847+
/// Indicates an outlives constraint between two free-regions declared
1848+
/// on the closure.
1849+
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1850+
pub struct ClosureOutlivesRequirement {
1851+
// This region ...
1852+
pub free_region: ty::RegionVid,
1853+
1854+
// .. must outlive this one.
1855+
pub outlived_free_region: ty::RegionVid,
1856+
1857+
// If not, report an error here.
1858+
pub blame_span: Span,
1859+
}
1860+
17921861
/*
17931862
* TypeFoldable implementations for MIR types
17941863
*/

src/librustc/ty/maps/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,10 @@ define_maps! { <'tcx>
190190
[] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (),
191191

192192
[] fn borrowck: BorrowCheck(DefId) -> Rc<BorrowCheckResult>,
193-
// FIXME: shouldn't this return a `Result<(), BorrowckErrors>` instead?
194-
[] fn mir_borrowck: MirBorrowCheck(DefId) -> (),
193+
194+
/// Borrow checks the function body. If this is a closure, returns
195+
/// additional requirements that the closure's creator must verify.
196+
[] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements>,
195197

196198
/// Gets a complete map from all types to their inherent impls.
197199
/// Not meant to be used directly outside of coherence.

src/librustc/ty/sty.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,17 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
646646
pub struct Binder<T>(pub T);
647647

648648
impl<T> Binder<T> {
649+
/// Wraps `value` in a binder, asserting that `value` does not
650+
/// contain any bound regions that would be bound by the
651+
/// binder. This is commonly used to 'inject' a value T into a
652+
/// different binding level.
653+
pub fn new_not_binding<'tcx>(value: T) -> Binder<T>
654+
where T: TypeFoldable<'tcx>
655+
{
656+
assert!(!value.has_escaping_regions());
657+
Binder(value)
658+
}
659+
649660
/// Skips the binder and returns the "bound" value. This is a
650661
/// risky thing to do because it's easy to get confused about
651662
/// debruijn indices and the like. It is usually better to
@@ -700,6 +711,32 @@ impl<T> Binder<T> {
700711
Some(self.skip_binder().clone())
701712
}
702713
}
714+
715+
/// Given two things that have the same binder level,
716+
/// and an operation that wraps on their contents, execute the operation
717+
/// and then wrap its result.
718+
///
719+
/// `f` should consider bound regions at depth 1 to be free, and
720+
/// anything it produces with bound regions at depth 1 will be
721+
/// bound in the resulting return value.
722+
pub fn fuse<U,F,R>(self, u: Binder<U>, f: F) -> Binder<R>
723+
where F: FnOnce(T, U) -> R
724+
{
725+
ty::Binder(f(self.0, u.0))
726+
}
727+
728+
/// Split the contents into two things that share the same binder
729+
/// level as the original, returning two distinct binders.
730+
///
731+
/// `f` should consider bound regions at depth 1 to be free, and
732+
/// anything it produces with bound regions at depth 1 will be
733+
/// bound in the resulting return values.
734+
pub fn split<U,V,F>(self, f: F) -> (Binder<U>, Binder<V>)
735+
where F: FnOnce(T) -> (U, V)
736+
{
737+
let (u, v) = f(self.0);
738+
(ty::Binder(u), ty::Binder(v))
739+
}
703740
}
704741

705742
/// Represents the projection of an associated type. In explicit UFCS
@@ -799,6 +836,9 @@ impl<'tcx> PolyFnSig<'tcx> {
799836
pub fn input(&self, index: usize) -> ty::Binder<Ty<'tcx>> {
800837
self.map_bound_ref(|fn_sig| fn_sig.inputs()[index])
801838
}
839+
pub fn inputs_and_output(&self) -> ty::Binder<&'tcx Slice<Ty<'tcx>>> {
840+
self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output)
841+
}
802842
pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
803843
self.map_bound_ref(|fn_sig| fn_sig.output().clone())
804844
}

src/librustc_driver/driver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController,
10691069

10701070
time(time_passes,
10711071
"MIR borrow checking",
1072-
|| for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id) });
1072+
|| for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id); });
10731073

10741074
time(time_passes,
10751075
"MIR effect checking",

src/librustc_mir/borrow_check/mod.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc::ty::maps::Providers;
1919
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
2020
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
2121
use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
22+
use rustc::mir::ClosureRegionRequirements;
2223

2324
use rustc_data_structures::fx::FxHashSet;
2425
use rustc_data_structures::indexed_set::{self, IdxSetBuf};
@@ -51,29 +52,34 @@ pub fn provide(providers: &mut Providers) {
5152
};
5253
}
5354

54-
fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
55+
fn mir_borrowck<'a, 'tcx>(
56+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
57+
def_id: DefId,
58+
) -> Option<ClosureRegionRequirements> {
5559
let input_mir = tcx.mir_validated(def_id);
5660
debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
5761

5862
if {
5963
!tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir()
6064
&& !tcx.sess.opts.debugging_opts.nll
6165
} {
62-
return;
66+
return None;
6367
}
6468

65-
tcx.infer_ctxt().enter(|infcx| {
69+
let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
6670
let input_mir: &Mir = &input_mir.borrow();
67-
do_mir_borrowck(&infcx, input_mir, def_id);
71+
do_mir_borrowck(&infcx, input_mir, def_id)
6872
});
6973
debug!("mir_borrowck done");
74+
75+
opt_closure_req
7076
}
7177

7278
fn do_mir_borrowck<'a, 'gcx, 'tcx>(
7379
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
7480
input_mir: &Mir<'gcx>,
7581
def_id: DefId,
76-
) {
82+
) -> Option<ClosureRegionRequirements> {
7783
let tcx = infcx.tcx;
7884
let attributes = tcx.get_attrs(def_id);
7985
let param_env = tcx.param_env(def_id);
@@ -91,7 +97,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
9197
let mir = &mut mir;
9298

9399
// Replace all regions with fresh inference variables.
94-
Some(nll::replace_regions_in_mir(infcx, def_id, mir))
100+
Some(nll::replace_regions_in_mir(infcx, def_id, param_env, mir))
95101
};
96102
let mir = &mir;
97103

@@ -177,19 +183,20 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
177183
));
178184

179185
// If we are in non-lexical mode, compute the non-lexical lifetimes.
180-
let opt_regioncx = if let Some(free_regions) = free_regions {
181-
Some(nll::compute_regions(
186+
let (opt_regioncx, opt_closure_req) = if let Some(free_regions) = free_regions {
187+
let (regioncx, opt_closure_req) = nll::compute_regions(
182188
infcx,
183189
def_id,
184190
free_regions,
185191
mir,
186192
param_env,
187193
&mut flow_inits,
188194
&mdpe.move_data,
189-
))
195+
);
196+
(Some(regioncx), opt_closure_req)
190197
} else {
191198
assert!(!tcx.sess.opts.debugging_opts.nll);
192-
None
199+
(None, None)
193200
};
194201
let flow_inits = flow_inits; // remove mut
195202

@@ -226,6 +233,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
226233
);
227234

228235
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
236+
237+
opt_closure_req
229238
}
230239

231240
#[allow(dead_code)]

src/librustc_mir/borrow_check/nll/mod.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use rustc::hir::def_id::DefId;
12-
use rustc::mir::Mir;
12+
use rustc::mir::{ClosureRegionRequirements, Mir};
1313
use rustc::infer::InferCtxt;
1414
use rustc::ty::{self, RegionKind, RegionVid};
1515
use rustc::util::nodemap::FxHashMap;
@@ -35,20 +35,21 @@ use self::region_infer::RegionInferenceContext;
3535
mod renumber;
3636

3737
/// Rewrites the regions in the MIR to use NLL variables, also
38-
/// scraping out the set of free regions (e.g., region parameters)
38+
/// scraping out the set of universal regions (e.g., region parameters)
3939
/// declared on the function. That set will need to be given to
4040
/// `compute_regions`.
4141
pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
4242
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
4343
def_id: DefId,
44+
param_env: ty::ParamEnv<'tcx>,
4445
mir: &mut Mir<'tcx>,
4546
) -> UniversalRegions<'tcx> {
4647
debug!("replace_regions_in_mir(def_id={:?})", def_id);
4748

48-
// Compute named region information.
49-
let universal_regions = universal_regions::universal_regions(infcx, def_id);
49+
// Compute named region information. This also renumbers the inputs/outputs.
50+
let universal_regions = UniversalRegions::new(infcx, def_id, param_env);
5051

51-
// Replace all regions with fresh inference variables.
52+
// Replace all remaining regions with fresh inference variables.
5253
renumber::renumber_mir(infcx, &universal_regions, mir);
5354

5455
let source = MirSource::item(def_id);
@@ -68,21 +69,19 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
6869
param_env: ty::ParamEnv<'gcx>,
6970
flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
7071
move_data: &MoveData<'tcx>,
71-
) -> RegionInferenceContext<'tcx> {
72+
) -> (
73+
RegionInferenceContext<'tcx>,
74+
Option<ClosureRegionRequirements>,
75+
) {
7276
// Run the MIR type-checker.
7377
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
7478
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
7579

7680
// Create the region inference context, taking ownership of the region inference
7781
// data that was contained in `infcx`.
7882
let var_origins = infcx.take_region_var_origins();
79-
let mut regioncx = RegionInferenceContext::new(var_origins, &universal_regions, mir);
80-
subtype_constraint_generation::generate(
81-
&mut regioncx,
82-
&universal_regions,
83-
mir,
84-
constraint_sets,
85-
);
83+
let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
84+
subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets);
8685

8786
// Compute what is live where.
8887
let liveness = &LivenessResults {
@@ -115,13 +114,13 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
115114
);
116115

117116
// Solve the region constraints.
118-
regioncx.solve(infcx, &mir);
117+
let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
119118

120119
// Dump MIR results into a file, if that is enabled. This let us
121120
// write unit-tests.
122121
dump_mir_results(infcx, liveness, MirSource::item(def_id), &mir, &regioncx);
123122

124-
regioncx
123+
(regioncx, closure_region_requirements)
125124
}
126125

127126
struct LivenessResults {
@@ -197,7 +196,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
197196
/// Right now, we piggy back on the `ReVar` to store our NLL inference
198197
/// regions. These are indexed with `RegionVid`. This method will
199198
/// assert that the region is a `ReVar` and extract its interal index.
200-
/// This is reasonable because in our MIR we replace all free regions
199+
/// This is reasonable because in our MIR we replace all universal regions
201200
/// with inference variables.
202201
pub trait ToRegionVid {
203202
fn to_region_vid(&self) -> RegionVid;

0 commit comments

Comments
 (0)