Skip to content

Commit a8437a0

Browse files
committed
Auto merge of #46509 - nikomatsakis:nll-master-to-rust-master-3, r=arielb1
closure handling for NLL This branch overhauls how we handle closures and universally quantified regions in the NLL code. The goal is to lay the groundwork for "region erasure" by the HIR-based type checker, as well as to avoid "crazy hacks" when it comes to closures. This means that when we type-check a closure, we cannot make use of *any* of the precise values of free regions in its signature, since those are inferred by the HIR type-checker. Therefore, the new code handles closures by: - Creating fresh regions for every free region that appears in the closure's type, which now includes both its signature and the types of all upvars. - This means that the closure is type-checked without knowing about the connections. - When we encounter some region relationship that we can't locally verify, we propagate it to the closure's creator. - During MIR typeck, the closure creators then validates those relationships. For a good example and explanation, see e.g. the test `src/test/nll/closure-requirements/propagate-despite-same-free-region.rs`. Upcoming changes in the next NLL PR (not included in this PR in order to keep it manageable): - Improvements to the MIR type checker so that it handles a lot of stuff presently overlooked - Refactor how we store region values to make it more efficient, better encapsulated - Propagate "type-outlives" relationships like `T: 'a` in a similar fashion to the one covered in this PR (still in the works, but close) - Improvements to error reporting (still in the works) r? @arielb1 or @pnkfelix
2 parents ee25791 + 1db58d7 commit a8437a0

File tree

79 files changed

+3288
-355
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+3288
-355
lines changed

src/librustc/ich/impls_mir.rs

+12
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

+10
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

+17-32
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> {
@@ -1475,38 +1480,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
14751480
closure_kind_ty.to_opt_closure_kind()
14761481
}
14771482

1478-
/// Obtain the signature of a function or closure.
1479-
/// For closures, unlike `tcx.fn_sig(def_id)`, this method will
1480-
/// work during the type-checking of the enclosing function and
1481-
/// return the closure signature in its partially inferred state.
1482-
pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
1483-
// Do we have an in-progress set of tables we are inferring?
1484-
if let Some(tables) = self.in_progress_tables {
1485-
// Is this a local item?
1486-
if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
1487-
// Is it a local *closure*?
1488-
if self.tcx.is_closure(def_id) {
1489-
let hir_id = self.tcx.hir.node_to_hir_id(id);
1490-
// Is this local closure contained within the tables we are inferring?
1491-
if tables.borrow().local_id_root == Some(DefId::local(hir_id.owner)) {
1492-
// if so, extract signature from there.
1493-
let closure_ty = tables.borrow().node_id_to_type(hir_id);
1494-
let (closure_def_id, closure_substs) = match closure_ty.sty {
1495-
ty::TyClosure(closure_def_id, closure_substs) =>
1496-
(closure_def_id, closure_substs),
1497-
_ =>
1498-
bug!("closure with non-closure type: {:?}", closure_ty),
1499-
};
1500-
assert_eq!(def_id, closure_def_id);
1501-
let closure_sig_ty = closure_substs.closure_sig_ty(def_id, self.tcx);
1502-
let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
1503-
return closure_sig_ty.fn_sig(self.tcx);
1504-
}
1505-
}
1506-
}
1507-
}
1508-
1509-
self.tcx.fn_sig(def_id)
1483+
/// Obtain the signature of a closure. For closures, unlike
1484+
/// `tcx.fn_sig(def_id)`, this method will work during the
1485+
/// type-checking of the enclosing function and return the closure
1486+
/// signature in its partially inferred state.
1487+
pub fn closure_sig(
1488+
&self,
1489+
def_id: DefId,
1490+
substs: ty::ClosureSubsts<'tcx>
1491+
) -> ty::PolyFnSig<'tcx> {
1492+
let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx);
1493+
let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
1494+
closure_sig_ty.fn_sig(self.tcx)
15101495
}
15111496

15121497
/// Normalizes associated types in `value`, potentially returning

src/librustc/mir/mod.rs

+69
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/traits/project.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -1339,26 +1339,27 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>(
13391339
vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>)
13401340
-> Progress<'tcx>
13411341
{
1342-
let closure_typer = selcx.closure_typer();
1343-
let closure_type = closure_typer.fn_sig(vtable.closure_def_id)
1344-
.subst(selcx.tcx(), vtable.substs.substs);
1342+
let tcx = selcx.tcx();
1343+
let infcx = selcx.infcx();
1344+
let closure_sig_ty = vtable.substs.closure_sig_ty(vtable.closure_def_id, tcx);
1345+
let closure_sig = infcx.shallow_resolve(&closure_sig_ty).fn_sig(tcx);
13451346
let Normalized {
1346-
value: closure_type,
1347+
value: closure_sig,
13471348
obligations
13481349
} = normalize_with_depth(selcx,
13491350
obligation.param_env,
13501351
obligation.cause.clone(),
13511352
obligation.recursion_depth+1,
1352-
&closure_type);
1353+
&closure_sig);
13531354

1354-
debug!("confirm_closure_candidate: obligation={:?},closure_type={:?},obligations={:?}",
1355+
debug!("confirm_closure_candidate: obligation={:?},closure_sig={:?},obligations={:?}",
13551356
obligation,
1356-
closure_type,
1357+
closure_sig,
13571358
obligations);
13581359

13591360
confirm_callable_candidate(selcx,
13601361
obligation,
1361-
closure_type,
1362+
closure_sig,
13621363
util::TupleArgumentsFlag::No)
13631364
.with_addl_obligations(vtable.nested)
13641365
.with_addl_obligations(obligations)

src/librustc/traits/select.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -3183,8 +3183,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
31833183
substs: ty::ClosureSubsts<'tcx>)
31843184
-> ty::PolyTraitRef<'tcx>
31853185
{
3186-
let closure_type = self.infcx.fn_sig(closure_def_id)
3187-
.subst(self.tcx(), substs.substs);
3186+
let closure_type = self.infcx.closure_sig(closure_def_id, substs);
31883187
let ty::Binder((trait_ref, _)) =
31893188
self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
31903189
obligation.predicate.0.self_ty(), // (1)

src/librustc/ty/instance.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use hir::def_id::DefId;
1212
use ty::{self, Ty, TypeFoldable, Substs, TyCtxt};
13-
use ty::subst::{Kind, Subst};
13+
use ty::subst::Kind;
1414
use traits;
1515
use syntax::abi::Abi;
1616
use util::ppaux;
@@ -311,7 +311,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
311311
let self_ty = tcx.mk_closure_from_closure_substs(
312312
closure_did, substs);
313313

314-
let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs);
314+
let sig = substs.closure_sig(closure_did, tcx);
315315
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
316316
assert_eq!(sig.inputs().len(), 1);
317317
let substs = tcx.mk_substs([

src/librustc/ty/maps/mod.rs

+4-2
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

+44
Original file line numberDiff line numberDiff line change
@@ -356,13 +356,17 @@ impl<'tcx> ClosureSubsts<'tcx> {
356356
/// Returns the closure kind for this closure; only usable outside
357357
/// of an inference context, because in that context we know that
358358
/// there are no type variables.
359+
///
360+
/// If you have an inference context, use `infcx.closure_kind()`.
359361
pub fn closure_kind(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::ClosureKind {
360362
self.split(def_id, tcx).closure_kind_ty.to_opt_closure_kind().unwrap()
361363
}
362364

363365
/// Extracts the signature from the closure; only usable outside
364366
/// of an inference context, because in that context we know that
365367
/// there are no type variables.
368+
///
369+
/// If you have an inference context, use `infcx.closure_sig()`.
366370
pub fn closure_sig(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::PolyFnSig<'tcx> {
367371
match self.closure_sig_ty(def_id, tcx).sty {
368372
ty::TyFnPtr(sig) => sig,
@@ -646,6 +650,17 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
646650
pub struct Binder<T>(pub T);
647651

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

705746
/// Represents the projection of an associated type. In explicit UFCS
@@ -799,6 +840,9 @@ impl<'tcx> PolyFnSig<'tcx> {
799840
pub fn input(&self, index: usize) -> ty::Binder<Ty<'tcx>> {
800841
self.map_bound_ref(|fn_sig| fn_sig.inputs()[index])
801842
}
843+
pub fn inputs_and_output(&self) -> ty::Binder<&'tcx Slice<Ty<'tcx>>> {
844+
self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output)
845+
}
802846
pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
803847
self.map_bound_ref(|fn_sig| fn_sig.output().clone())
804848
}

src/librustc_driver/driver.rs

+1-1
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",

0 commit comments

Comments
 (0)