Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

closure handling for NLL #46509

Merged
merged 21 commits into from
Dec 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e5dc4ba
renumber types in `ty::Const` and relate them to `mir::Constant`
nikomatsakis Nov 22, 2017
2ec959f
extend MIR dump with detailed, extra information
nikomatsakis Nov 22, 2017
ab1c1bc
mir-borrowck returns closure requirements, mir-typeck enforces
nikomatsakis Nov 22, 2017
05441ab
add closure requirement tests, improve debugging output
nikomatsakis Nov 22, 2017
d5ef3e2
replace `InferCtxt::fn_sig` with `closure_sig`
nikomatsakis Dec 1, 2017
b4d71ea
make `fn_sig().subst()` ICE when used with a closure
nikomatsakis Dec 1, 2017
681f54f
fix filter to support `&` and `|`
nikomatsakis Nov 17, 2017
47b8ef5
don't print self symbol's internal index unless gensymed
nikomatsakis Dec 4, 2017
05977fe
rewrite comment on how to get closure sig, per arielb1's suggestion
nikomatsakis Dec 6, 2017
dac59ac
escape-argument-callee: improve comment per arielb1's suggestions
nikomatsakis Dec 6, 2017
782b9f5
escape-argument: improve comment per arielb1's suggestion
nikomatsakis Dec 6, 2017
8b657d3
escape-upvar-nested: patch "dangling reference" to renamed test
nikomatsakis Dec 6, 2017
6ee31e2
escape-upvar-ref: patch comment per arielb1
nikomatsakis Dec 6, 2017
3c42f18
delete stray file
nikomatsakis Dec 6, 2017
ca60826
test approximations of the "shorter side" that resort to `'static`
nikomatsakis Dec 6, 2017
2a17b93
test more failure cases for approximating the "longer" side
nikomatsakis Dec 6, 2017
b8e9eaf
propagate-approximated-ref: include FIXME comment
nikomatsakis Dec 6, 2017
75cf482
region-ebr-does-not-outlive-static: reuse old test instead
nikomatsakis Dec 6, 2017
86e7b5c
use `-Znll -Zborrowck=mir -Zverbose` consistently
nikomatsakis Dec 6, 2017
4703770
add test comparing free region to bound region
nikomatsakis Dec 7, 2017
1db58d7
rename `Binder::new_not_binding` to `Binder::dummy`
nikomatsakis Dec 7, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/librustc/ich/impls_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,3 +572,15 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Literal<'gcx> {
}

impl_stable_hash_for!(struct mir::Location { block, statement_index });

impl_stable_hash_for!(struct mir::ClosureRegionRequirements {
num_external_vids,
outlives_requirements
});

impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement {
free_region,
outlived_free_region,
blame_span
});

10 changes: 10 additions & 0 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ for ty::RegionKind {
}
}

impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::RegionVid {
#[inline]
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
use rustc_data_structures::indexed_vec::Idx;
self.index().hash_stable(hcx, hasher);
}
}

impl<'gcx> HashStable<StableHashingContext<'gcx>>
for ty::adjustment::AutoBorrow<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,
Expand Down
49 changes: 17 additions & 32 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin)))
}

/// Number of region variables created so far.
pub fn num_region_vars(&self) -> usize {
self.borrow_region_constraints().var_origins().len()
}

/// Just a convenient wrapper of `next_region_var` for using during NLL.
pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin)
-> ty::Region<'tcx> {
Expand Down Expand Up @@ -1475,38 +1480,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
closure_kind_ty.to_opt_closure_kind()
}

/// Obtain the signature of a function or closure.
/// For closures, unlike `tcx.fn_sig(def_id)`, this method will
/// work during the type-checking of the enclosing function and
/// return the closure signature in its partially inferred state.
pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
// Do we have an in-progress set of tables we are inferring?
if let Some(tables) = self.in_progress_tables {
// Is this a local item?
if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
// Is it a local *closure*?
if self.tcx.is_closure(def_id) {
let hir_id = self.tcx.hir.node_to_hir_id(id);
// Is this local closure contained within the tables we are inferring?
if tables.borrow().local_id_root == Some(DefId::local(hir_id.owner)) {
// if so, extract signature from there.
let closure_ty = tables.borrow().node_id_to_type(hir_id);
let (closure_def_id, closure_substs) = match closure_ty.sty {
ty::TyClosure(closure_def_id, closure_substs) =>
(closure_def_id, closure_substs),
_ =>
bug!("closure with non-closure type: {:?}", closure_ty),
};
assert_eq!(def_id, closure_def_id);
let closure_sig_ty = closure_substs.closure_sig_ty(def_id, self.tcx);
let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
return closure_sig_ty.fn_sig(self.tcx);
}
}
}
}

self.tcx.fn_sig(def_id)
/// Obtain the signature of a closure. For closures, unlike
/// `tcx.fn_sig(def_id)`, this method will work during the
/// type-checking of the enclosing function and return the closure
/// signature in its partially inferred state.
pub fn closure_sig(
&self,
def_id: DefId,
substs: ty::ClosureSubsts<'tcx>
) -> ty::PolyFnSig<'tcx> {
let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx);
let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
closure_sig_ty.fn_sig(self.tcx)
}

/// Normalizes associated types in `value`, potentially returning
Expand Down
69 changes: 69 additions & 0 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1789,6 +1789,75 @@ pub struct GeneratorLayout<'tcx> {
pub fields: Vec<LocalDecl<'tcx>>,
}

/// After we borrow check a closure, we are left with various
/// requirements that we have inferred between the free regions that
/// appear in the closure's signature or on its field types. These
/// requirements are then verified and proved by the closure's
/// creating function. This struct encodes those requirements.
///
/// The requirements are listed as being between various
/// `RegionVid`. The 0th region refers to `'static`; subsequent region
/// vids refer to the free regions that appear in the closure (or
/// generator's) type, in order of appearance. (This numbering is
/// actually defined by the `UniversalRegions` struct in the NLL
/// region checker. See for example
/// `UniversalRegions::closure_mapping`.) Note that we treat the free
/// regions in the closure's type "as if" they were erased, so their
/// precise identity is not important, only their position.
///
/// Example: If type check produces a closure with the closure substs:
///
/// ```
/// ClosureSubsts = [
/// i8, // the "closure kind"
/// for<'x> fn(&'a &'x u32) -> &'x u32, // the "closure signature"
/// &'a String, // some upvar
/// ]
/// ```
///
/// here, there is one unique free region (`'a`) but it appears
/// twice. We would "renumber" each occurence to a unique vid, as follows:
///
/// ```
/// ClosureSubsts = [
/// i8, // the "closure kind"
/// for<'x> fn(&'1 &'x u32) -> &'x u32, // the "closure signature"
/// &'2 String, // some upvar
/// ]
/// ```
///
/// Now the code might impose a requirement like `'1: '2`. When an
/// instance of the closure is created, the corresponding free regions
/// can be extracted from its type and constrained to have the given
/// outlives relationship.
#[derive(Clone, Debug)]
pub struct ClosureRegionRequirements {
/// The number of external regions defined on the closure. In our
/// example above, it would be 3 -- one for `'static`, then `'1`
/// and `'2`. This is just used for a sanity check later on, to
/// make sure that the number of regions we see at the callsite
/// matches.
pub num_external_vids: usize,

/// Requirements between the various free regions defined in
/// indices.
pub outlives_requirements: Vec<ClosureOutlivesRequirement>,
}

/// Indicates an outlives constraint between two free-regions declared
/// on the closure.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ClosureOutlivesRequirement {
// This region ...
pub free_region: ty::RegionVid,

// .. must outlive this one.
pub outlived_free_region: ty::RegionVid,

// If not, report an error here.
pub blame_span: Span,
}

/*
* TypeFoldable implementations for MIR types
*/
Expand Down
17 changes: 9 additions & 8 deletions src/librustc/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1339,26 +1339,27 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>(
vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>)
-> Progress<'tcx>
{
let closure_typer = selcx.closure_typer();
let closure_type = closure_typer.fn_sig(vtable.closure_def_id)
.subst(selcx.tcx(), vtable.substs.substs);
let tcx = selcx.tcx();
let infcx = selcx.infcx();
let closure_sig_ty = vtable.substs.closure_sig_ty(vtable.closure_def_id, tcx);
let closure_sig = infcx.shallow_resolve(&closure_sig_ty).fn_sig(tcx);
let Normalized {
value: closure_type,
value: closure_sig,
obligations
} = normalize_with_depth(selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth+1,
&closure_type);
&closure_sig);

debug!("confirm_closure_candidate: obligation={:?},closure_type={:?},obligations={:?}",
debug!("confirm_closure_candidate: obligation={:?},closure_sig={:?},obligations={:?}",
obligation,
closure_type,
closure_sig,
obligations);

confirm_callable_candidate(selcx,
obligation,
closure_type,
closure_sig,
util::TupleArgumentsFlag::No)
.with_addl_obligations(vtable.nested)
.with_addl_obligations(obligations)
Expand Down
3 changes: 1 addition & 2 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3183,8 +3183,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
substs: ty::ClosureSubsts<'tcx>)
-> ty::PolyTraitRef<'tcx>
{
let closure_type = self.infcx.fn_sig(closure_def_id)
.subst(self.tcx(), substs.substs);
let closure_type = self.infcx.closure_sig(closure_def_id, substs);
let ty::Binder((trait_ref, _)) =
self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
obligation.predicate.0.self_ty(), // (1)
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use hir::def_id::DefId;
use ty::{self, Ty, TypeFoldable, Substs, TyCtxt};
use ty::subst::{Kind, Subst};
use ty::subst::Kind;
use traits;
use syntax::abi::Abi;
use util::ppaux;
Expand Down Expand Up @@ -311,7 +311,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
let self_ty = tcx.mk_closure_from_closure_substs(
closure_did, substs);

let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs);
let sig = substs.closure_sig(closure_did, tcx);
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
assert_eq!(sig.inputs().len(), 1);
let substs = tcx.mk_substs([
Expand Down
6 changes: 4 additions & 2 deletions src/librustc/ty/maps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,10 @@ define_maps! { <'tcx>
[] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (),

[] fn borrowck: BorrowCheck(DefId) -> Rc<BorrowCheckResult>,
// FIXME: shouldn't this return a `Result<(), BorrowckErrors>` instead?
[] fn mir_borrowck: MirBorrowCheck(DefId) -> (),

/// Borrow checks the function body. If this is a closure, returns
/// additional requirements that the closure's creator must verify.
[] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements>,

/// Gets a complete map from all types to their inherent impls.
/// Not meant to be used directly outside of coherence.
Expand Down
44 changes: 44 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,17 @@ impl<'tcx> ClosureSubsts<'tcx> {
/// Returns the closure kind for this closure; only usable outside
/// of an inference context, because in that context we know that
/// there are no type variables.
///
/// If you have an inference context, use `infcx.closure_kind()`.
pub fn closure_kind(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::ClosureKind {
self.split(def_id, tcx).closure_kind_ty.to_opt_closure_kind().unwrap()
}

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

impl<T> Binder<T> {
/// Wraps `value` in a binder, asserting that `value` does not
/// contain any bound regions that would be bound by the
/// binder. This is commonly used to 'inject' a value T into a
/// different binding level.
pub fn dummy<'tcx>(value: T) -> Binder<T>
where T: TypeFoldable<'tcx>
{
assert!(!value.has_escaping_regions());
Binder(value)
}

/// Skips the binder and returns the "bound" value. This is a
/// risky thing to do because it's easy to get confused about
/// debruijn indices and the like. It is usually better to
Expand Down Expand Up @@ -700,6 +715,32 @@ impl<T> Binder<T> {
Some(self.skip_binder().clone())
}
}

/// Given two things that have the same binder level,
/// and an operation that wraps on their contents, execute the operation
/// and then wrap its result.
///
/// `f` should consider bound regions at depth 1 to be free, and
/// anything it produces with bound regions at depth 1 will be
/// bound in the resulting return value.
pub fn fuse<U,F,R>(self, u: Binder<U>, f: F) -> Binder<R>
where F: FnOnce(T, U) -> R
{
ty::Binder(f(self.0, u.0))
}

/// Split the contents into two things that share the same binder
/// level as the original, returning two distinct binders.
///
/// `f` should consider bound regions at depth 1 to be free, and
/// anything it produces with bound regions at depth 1 will be
/// bound in the resulting return values.
pub fn split<U,V,F>(self, f: F) -> (Binder<U>, Binder<V>)
where F: FnOnce(T) -> (U, V)
{
let (u, v) = f(self.0);
(ty::Binder(u), ty::Binder(v))
}
}

/// Represents the projection of an associated type. In explicit UFCS
Expand Down Expand Up @@ -799,6 +840,9 @@ impl<'tcx> PolyFnSig<'tcx> {
pub fn input(&self, index: usize) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.inputs()[index])
}
pub fn inputs_and_output(&self) -> ty::Binder<&'tcx Slice<Ty<'tcx>>> {
self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output)
}
pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.output().clone())
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController,

time(time_passes,
"MIR borrow checking",
|| for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id) });
|| for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id); });

time(time_passes,
"MIR effect checking",
Expand Down
Loading