mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
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
This commit is contained in:
commit
a8437a0acc
@ -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
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
@ -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([
|
||||
|
@ -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.
|
||||
|
@ -356,6 +356,8 @@ 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()
|
||||
}
|
||||
@ -363,6 +365,8 @@ impl<'tcx> ClosureSubsts<'tcx> {
|
||||
/// 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,
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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())
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -19,6 +19,7 @@ use rustc::ty::maps::Providers;
|
||||
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
|
||||
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
|
||||
use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
|
||||
use rustc::mir::ClosureRegionRequirements;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::indexed_set::{self, IdxSetBuf};
|
||||
@ -51,7 +52,10 @@ pub fn provide(providers: &mut Providers) {
|
||||
};
|
||||
}
|
||||
|
||||
fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
|
||||
fn mir_borrowck<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
) -> Option<ClosureRegionRequirements> {
|
||||
let input_mir = tcx.mir_validated(def_id);
|
||||
debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
|
||||
|
||||
@ -59,21 +63,23 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
|
||||
!tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir()
|
||||
&& !tcx.sess.opts.debugging_opts.nll
|
||||
} {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
tcx.infer_ctxt().enter(|infcx| {
|
||||
let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
|
||||
let input_mir: &Mir = &input_mir.borrow();
|
||||
do_mir_borrowck(&infcx, input_mir, def_id);
|
||||
do_mir_borrowck(&infcx, input_mir, def_id)
|
||||
});
|
||||
debug!("mir_borrowck done");
|
||||
|
||||
opt_closure_req
|
||||
}
|
||||
|
||||
fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
input_mir: &Mir<'gcx>,
|
||||
def_id: DefId,
|
||||
) {
|
||||
) -> Option<ClosureRegionRequirements> {
|
||||
let tcx = infcx.tcx;
|
||||
let attributes = tcx.get_attrs(def_id);
|
||||
let param_env = tcx.param_env(def_id);
|
||||
@ -91,7 +97,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
let mir = &mut mir;
|
||||
|
||||
// Replace all regions with fresh inference variables.
|
||||
Some(nll::replace_regions_in_mir(infcx, def_id, mir))
|
||||
Some(nll::replace_regions_in_mir(infcx, def_id, param_env, mir))
|
||||
};
|
||||
let mir = &mir;
|
||||
|
||||
@ -177,8 +183,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
));
|
||||
|
||||
// If we are in non-lexical mode, compute the non-lexical lifetimes.
|
||||
let opt_regioncx = if let Some(free_regions) = free_regions {
|
||||
Some(nll::compute_regions(
|
||||
let (opt_regioncx, opt_closure_req) = if let Some(free_regions) = free_regions {
|
||||
let (regioncx, opt_closure_req) = nll::compute_regions(
|
||||
infcx,
|
||||
def_id,
|
||||
free_regions,
|
||||
@ -186,10 +192,11 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
param_env,
|
||||
&mut flow_inits,
|
||||
&mdpe.move_data,
|
||||
))
|
||||
);
|
||||
(Some(regioncx), opt_closure_req)
|
||||
} else {
|
||||
assert!(!tcx.sess.opts.debugging_opts.nll);
|
||||
None
|
||||
(None, None)
|
||||
};
|
||||
let flow_inits = flow_inits; // remove mut
|
||||
|
||||
@ -226,6 +233,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
);
|
||||
|
||||
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
|
||||
|
||||
opt_closure_req
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -9,11 +9,12 @@
|
||||
// except according to those terms.
|
||||
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::mir::Mir;
|
||||
use rustc::mir::{ClosureRegionRequirements, Mir};
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::ty::{self, RegionKind, RegionVid};
|
||||
use rustc::util::nodemap::FxHashMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::io;
|
||||
use transform::MirSource;
|
||||
use transform::type_check;
|
||||
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
|
||||
@ -22,6 +23,7 @@ use dataflow::MaybeInitializedLvals;
|
||||
use dataflow::move_paths::MoveData;
|
||||
|
||||
use util as mir_util;
|
||||
use util::pretty::{self, ALIGN};
|
||||
use self::mir_util::PassWhere;
|
||||
|
||||
mod constraint_generation;
|
||||
@ -35,20 +37,26 @@ use self::region_infer::RegionInferenceContext;
|
||||
mod renumber;
|
||||
|
||||
/// Rewrites the regions in the MIR to use NLL variables, also
|
||||
/// scraping out the set of free regions (e.g., region parameters)
|
||||
/// scraping out the set of universal regions (e.g., region parameters)
|
||||
/// declared on the function. That set will need to be given to
|
||||
/// `compute_regions`.
|
||||
pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
mir: &mut Mir<'tcx>,
|
||||
) -> UniversalRegions<'tcx> {
|
||||
// Compute named region information.
|
||||
let universal_regions = universal_regions::universal_regions(infcx, def_id);
|
||||
debug!("replace_regions_in_mir(def_id={:?})", def_id);
|
||||
|
||||
// Replace all regions with fresh inference variables.
|
||||
// Compute named region information. This also renumbers the inputs/outputs.
|
||||
let universal_regions = UniversalRegions::new(infcx, def_id, param_env);
|
||||
|
||||
// Replace all remaining regions with fresh inference variables.
|
||||
renumber::renumber_mir(infcx, &universal_regions, mir);
|
||||
|
||||
let source = MirSource::item(def_id);
|
||||
mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(()));
|
||||
|
||||
universal_regions
|
||||
}
|
||||
|
||||
@ -63,7 +71,10 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
||||
param_env: ty::ParamEnv<'gcx>,
|
||||
flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
) -> RegionInferenceContext<'tcx> {
|
||||
) -> (
|
||||
RegionInferenceContext<'tcx>,
|
||||
Option<ClosureRegionRequirements>,
|
||||
) {
|
||||
// Run the MIR type-checker.
|
||||
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
|
||||
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
|
||||
@ -71,13 +82,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
||||
// Create the region inference context, taking ownership of the region inference
|
||||
// data that was contained in `infcx`.
|
||||
let var_origins = infcx.take_region_var_origins();
|
||||
let mut regioncx = RegionInferenceContext::new(var_origins, &universal_regions, mir);
|
||||
subtype_constraint_generation::generate(
|
||||
&mut regioncx,
|
||||
&universal_regions,
|
||||
mir,
|
||||
constraint_sets,
|
||||
);
|
||||
let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
|
||||
subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets);
|
||||
|
||||
// Compute what is live where.
|
||||
let liveness = &LivenessResults {
|
||||
@ -110,13 +116,24 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
||||
);
|
||||
|
||||
// Solve the region constraints.
|
||||
regioncx.solve(infcx, &mir);
|
||||
let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
|
||||
|
||||
// Dump MIR results into a file, if that is enabled. This let us
|
||||
// write unit-tests.
|
||||
dump_mir_results(infcx, liveness, MirSource::item(def_id), &mir, ®ioncx);
|
||||
// write unit-tests, as well as helping with debugging.
|
||||
dump_mir_results(
|
||||
infcx,
|
||||
liveness,
|
||||
MirSource::item(def_id),
|
||||
&mir,
|
||||
®ioncx,
|
||||
&closure_region_requirements,
|
||||
);
|
||||
|
||||
regioncx
|
||||
// We also have a `#[rustc_nll]` annotation that causes us to dump
|
||||
// information
|
||||
dump_annotation(infcx, &mir, def_id, ®ioncx, &closure_region_requirements);
|
||||
|
||||
(regioncx, closure_region_requirements)
|
||||
}
|
||||
|
||||
struct LivenessResults {
|
||||
@ -130,6 +147,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
|
||||
source: MirSource,
|
||||
mir: &Mir<'tcx>,
|
||||
regioncx: &RegionInferenceContext,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements>,
|
||||
) {
|
||||
if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
|
||||
return;
|
||||
@ -164,9 +182,17 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
|
||||
mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| {
|
||||
match pass_where {
|
||||
// Before the CFG, dump out the values for each region variable.
|
||||
PassWhere::BeforeCFG => for region in regioncx.regions() {
|
||||
writeln!(out, "| {:?}: {}", region, regioncx.region_value_str(region))?;
|
||||
},
|
||||
PassWhere::BeforeCFG => {
|
||||
regioncx.dump_mir(out)?;
|
||||
|
||||
if let Some(closure_region_requirements) = closure_region_requirements {
|
||||
writeln!(out, "|")?;
|
||||
writeln!(out, "| Free Region Constraints")?;
|
||||
for_each_region_constraint(closure_region_requirements, &mut |msg| {
|
||||
writeln!(out, "| {}", msg)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
// Before each basic block, dump out the values
|
||||
// that are live on entry to the basic block.
|
||||
@ -180,19 +206,96 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
|
||||
®ular_liveness_per_location[&location],
|
||||
&drop_liveness_per_location[&location],
|
||||
);
|
||||
writeln!(out, " | Live variables at {:?}: {}", location, s)?;
|
||||
writeln!(
|
||||
out,
|
||||
"{:ALIGN$} | Live variables on entry to {:?}: {}",
|
||||
"",
|
||||
location,
|
||||
s,
|
||||
ALIGN = ALIGN
|
||||
)?;
|
||||
}
|
||||
|
||||
PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Also dump the inference graph constraints as a graphviz file.
|
||||
let _: io::Result<()> = do catch {
|
||||
let mut file =
|
||||
pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?;
|
||||
regioncx.dump_graphviz(&mut file)
|
||||
};
|
||||
}
|
||||
|
||||
fn dump_annotation<'a, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
mir_def_id: DefId,
|
||||
regioncx: &RegionInferenceContext,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements>,
|
||||
) {
|
||||
let tcx = infcx.tcx;
|
||||
let base_def_id = tcx.closure_base_def_id(mir_def_id);
|
||||
if !tcx.has_attr(base_def_id, "rustc_regions") {
|
||||
return;
|
||||
}
|
||||
|
||||
// When the enclosing function is tagged with `#[rustc_regions]`,
|
||||
// we dump out various bits of state as warnings. This is useful
|
||||
// for verifying that the compiler is behaving as expected. These
|
||||
// warnings focus on the closure region requirements -- for
|
||||
// viewing the intraprocedural state, the -Zdump-mir output is
|
||||
// better.
|
||||
|
||||
if let Some(closure_region_requirements) = closure_region_requirements {
|
||||
let mut err = tcx.sess
|
||||
.diagnostic()
|
||||
.span_note_diag(mir.span, "External requirements");
|
||||
|
||||
regioncx.annotate(&mut err);
|
||||
|
||||
err.note(&format!(
|
||||
"number of external vids: {}",
|
||||
closure_region_requirements.num_external_vids
|
||||
));
|
||||
|
||||
// Dump the region constraints we are imposing *between* those
|
||||
// newly created variables.
|
||||
for_each_region_constraint(closure_region_requirements, &mut |msg| {
|
||||
err.note(msg);
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
|
||||
err.emit();
|
||||
} else {
|
||||
let mut err = tcx.sess
|
||||
.diagnostic()
|
||||
.span_note_diag(mir.span, "No external requirements");
|
||||
regioncx.annotate(&mut err);
|
||||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_region_constraint(
|
||||
closure_region_requirements: &ClosureRegionRequirements,
|
||||
with_msg: &mut FnMut(&str) -> io::Result<()>,
|
||||
) -> io::Result<()> {
|
||||
for req in &closure_region_requirements.outlives_requirements {
|
||||
with_msg(&format!(
|
||||
"where {:?}: {:?}",
|
||||
req.free_region,
|
||||
req.outlived_free_region,
|
||||
))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Right now, we piggy back on the `ReVar` to store our NLL inference
|
||||
/// regions. These are indexed with `RegionVid`. This method will
|
||||
/// assert that the region is a `ReVar` and extract its interal index.
|
||||
/// This is reasonable because in our MIR we replace all free regions
|
||||
/// This is reasonable because in our MIR we replace all universal regions
|
||||
/// with inference variables.
|
||||
pub trait ToRegionVid {
|
||||
fn to_region_vid(&self) -> RegionVid;
|
||||
|
48
src/librustc_mir/borrow_check/nll/region_infer/annotation.rs
Normal file
48
src/librustc_mir/borrow_check/nll/region_infer/annotation.rs
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! As part of the NLL unit tests, you can annotate a function with
|
||||
//! `#[rustc_regions]`, and we will emit information about the region
|
||||
//! inference context and -- in particular -- the external constraints
|
||||
//! that this region imposes on others. The methods in this file
|
||||
//! handle the part about dumping the inference context internal
|
||||
//! state.
|
||||
|
||||
use rustc::ty;
|
||||
use rustc_errors::DiagnosticBuilder;
|
||||
use super::RegionInferenceContext;
|
||||
|
||||
impl<'gcx, 'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Write out our state into the `.mir` files.
|
||||
pub(crate) fn annotate(&self, err: &mut DiagnosticBuilder<'_>) {
|
||||
match self.universal_regions.defining_ty.sty {
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
err.note(&format!(
|
||||
"defining type: {:?} with closure substs {:#?}",
|
||||
def_id,
|
||||
&substs.substs[..]
|
||||
));
|
||||
}
|
||||
ty::TyFnDef(def_id, substs) => {
|
||||
err.note(&format!(
|
||||
"defining type: {:?} with substs {:#?}",
|
||||
def_id,
|
||||
&substs[..]
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
err.note(&format!(
|
||||
"defining type: {:?}",
|
||||
self.universal_regions.defining_ty
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs
Normal file
100
src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! As part of generating the regions, if you enable `-Zdump-mir=nll`,
|
||||
//! we will generate an annotated copy of the MIR that includes the
|
||||
//! state of region inference. This code handles emitting the region
|
||||
//! context internal state.
|
||||
|
||||
use std::io::{self, Write};
|
||||
use super::{Constraint, RegionInferenceContext};
|
||||
|
||||
// Room for "'_#NNNNr" before things get misaligned.
|
||||
// Easy enough to fix if this ever doesn't seem like
|
||||
// enough.
|
||||
const REGION_WIDTH: usize = 8;
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Write out our state into the `.mir` files.
|
||||
pub(crate) fn dump_mir(&self, out: &mut Write) -> io::Result<()> {
|
||||
writeln!(out, "| Free Region Mapping")?;
|
||||
|
||||
for region in self.regions() {
|
||||
if self.definitions[region].is_universal {
|
||||
let classification = self.universal_regions.region_classification(region).unwrap();
|
||||
let outlived_by = self.universal_regions.regions_outlived_by(region);
|
||||
writeln!(
|
||||
out,
|
||||
"| {r:rw$} | {c:cw$} | {ob}",
|
||||
r = format!("{:?}", region),
|
||||
rw = REGION_WIDTH,
|
||||
c = format!("{:?}", classification),
|
||||
cw = 8, // "External" at most
|
||||
ob = format!("{:?}", outlived_by)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(out, "|")?;
|
||||
writeln!(out, "| Inferred Region Values")?;
|
||||
for region in self.regions() {
|
||||
writeln!(
|
||||
out,
|
||||
"| {r:rw$} | {v}",
|
||||
r = format!("{:?}", region),
|
||||
rw = REGION_WIDTH,
|
||||
v = self.region_value_str(region),
|
||||
)?;
|
||||
}
|
||||
|
||||
writeln!(out, "|")?;
|
||||
writeln!(out, "| Inference Constraints")?;
|
||||
self.for_each_constraint(&mut |msg| writeln!(out, "| {}", msg))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Debugging aid: Invokes the `with_msg` callback repeatedly with
|
||||
/// our internal region constraints. These are dumped into the
|
||||
/// -Zdump-mir file so that we can figure out why the region
|
||||
/// inference resulted in the values that it did when debugging.
|
||||
fn for_each_constraint(
|
||||
&self,
|
||||
with_msg: &mut FnMut(&str) -> io::Result<()>,
|
||||
) -> io::Result<()> {
|
||||
for region in self.definitions.indices() {
|
||||
let value = self.region_value_str_from_matrix(&self.liveness_constraints, region);
|
||||
if value != "{}" {
|
||||
with_msg(&format!("{:?} live at {}", region, value))?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut constraints: Vec<_> = self.constraints.iter().collect();
|
||||
constraints.sort();
|
||||
for constraint in &constraints {
|
||||
let Constraint {
|
||||
sup,
|
||||
sub,
|
||||
point,
|
||||
span,
|
||||
} = constraint;
|
||||
with_msg(&format!(
|
||||
"{:?}: {:?} @ {:?} due to {:?}",
|
||||
sup,
|
||||
sub,
|
||||
point,
|
||||
span
|
||||
))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
71
src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
Normal file
71
src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! This module provides linkage between RegionInferenceContext and
|
||||
//! libgraphviz traits, specialized to attaching borrowck analysis
|
||||
//! data to rendered labels.
|
||||
|
||||
use dot::{self, IntoCow};
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use std::borrow::Cow;
|
||||
use std::io::{self, Write};
|
||||
use super::*;
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Write out the region constraint graph.
|
||||
pub(crate) fn dump_graphviz(&self, mut w: &mut Write) -> io::Result<()> {
|
||||
dot::render(self, &mut w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'this, 'tcx> dot::Labeller<'this> for RegionInferenceContext<'tcx> {
|
||||
type Node = RegionVid;
|
||||
type Edge = Constraint;
|
||||
|
||||
fn graph_id(&'this self) -> dot::Id<'this> {
|
||||
dot::Id::new(format!("RegionInferenceContext")).unwrap()
|
||||
}
|
||||
fn node_id(&'this self, n: &RegionVid) -> dot::Id<'this> {
|
||||
dot::Id::new(format!("r{}", n.index())).unwrap()
|
||||
}
|
||||
fn node_shape(&'this self, _node: &RegionVid) -> Option<dot::LabelText<'this>> {
|
||||
Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
|
||||
}
|
||||
fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
|
||||
dot::LabelText::LabelStr(format!("{:?}", n).into_cow())
|
||||
}
|
||||
fn edge_label(&'this self, e: &Constraint) -> dot::LabelText<'this> {
|
||||
dot::LabelText::LabelStr(format!("{:?}", e.point).into_cow())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'this, 'tcx> dot::GraphWalk<'this> for RegionInferenceContext<'tcx> {
|
||||
type Node = RegionVid;
|
||||
type Edge = Constraint;
|
||||
|
||||
fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> {
|
||||
let vids: Vec<RegionVid> = self.definitions.indices().collect();
|
||||
vids.into_cow()
|
||||
}
|
||||
fn edges(&'this self) -> dot::Edges<'this, Constraint> {
|
||||
(&self.constraints[..]).into_cow()
|
||||
}
|
||||
|
||||
// Render `a: b` as `a <- b`, indicating the flow
|
||||
// of data during inference.
|
||||
|
||||
fn source(&'this self, edge: &Constraint) -> RegionVid {
|
||||
edge.sub
|
||||
}
|
||||
|
||||
fn target(&'this self, edge: &Constraint) -> RegionVid {
|
||||
edge.sup
|
||||
}
|
||||
}
|
@ -9,12 +9,13 @@
|
||||
// except according to those terms.
|
||||
|
||||
use super::universal_regions::UniversalRegions;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::infer::RegionVariableOrigin;
|
||||
use rustc::infer::NLLRegionVariableOrigin;
|
||||
use rustc::infer::RegionVariableOrigin;
|
||||
use rustc::infer::SubregionOrigin;
|
||||
use rustc::infer::region_constraints::VarOrigins;
|
||||
use rustc::infer::outlives::free_region_map::FreeRegionMap;
|
||||
use rustc::mir::{Location, Mir};
|
||||
use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir};
|
||||
use rustc::ty::{self, RegionVid};
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
@ -24,6 +25,10 @@ use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use syntax_pos::Span;
|
||||
|
||||
mod annotation;
|
||||
mod dump_mir;
|
||||
mod graphviz;
|
||||
|
||||
pub struct RegionInferenceContext<'tcx> {
|
||||
/// Contains the definition for every region variable. Region
|
||||
/// variables are identified by their index (`RegionVid`). The
|
||||
@ -52,12 +57,9 @@ pub struct RegionInferenceContext<'tcx> {
|
||||
/// the free regions.)
|
||||
point_indices: BTreeMap<Location, usize>,
|
||||
|
||||
/// Number of universally quantified regions. This is used to
|
||||
/// determine the meaning of the bits in `inferred_values` and
|
||||
/// friends.
|
||||
num_universal_regions: usize,
|
||||
|
||||
free_region_map: &'tcx FreeRegionMap<'tcx>,
|
||||
/// Information about the universally quantified regions in scope
|
||||
/// on this function and their (known) relations to one another.
|
||||
universal_regions: UniversalRegions<'tcx>,
|
||||
}
|
||||
|
||||
struct RegionDefinition<'tcx> {
|
||||
@ -67,9 +69,15 @@ struct RegionDefinition<'tcx> {
|
||||
/// late-bound-regions).
|
||||
origin: RegionVariableOrigin,
|
||||
|
||||
/// If this is a free-region, then this is `Some(X)` where `X` is
|
||||
/// the name of the region.
|
||||
name: Option<ty::Region<'tcx>>,
|
||||
/// True if this is a universally quantified region. This means a
|
||||
/// lifetime parameter that appears in the function signature (or,
|
||||
/// in the case of a closure, in the closure environment, which of
|
||||
/// course is also in the function signature).
|
||||
is_universal: bool,
|
||||
|
||||
/// If this is 'static or an early-bound region, then this is
|
||||
/// `Some(X)` where `X` is the name of the region.
|
||||
external_name: Option<ty::Region<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
@ -98,11 +106,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// regions defined in `universal_regions`.
|
||||
pub fn new(
|
||||
var_origins: VarOrigins,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
universal_regions: UniversalRegions<'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
) -> Self {
|
||||
let num_region_variables = var_origins.len();
|
||||
let num_universal_regions = universal_regions.indices.len();
|
||||
let num_universal_regions = universal_regions.len();
|
||||
|
||||
let mut num_points = 0;
|
||||
let mut point_indices = BTreeMap::new();
|
||||
@ -133,11 +141,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
inferred_values: None,
|
||||
constraints: Vec::new(),
|
||||
point_indices,
|
||||
num_universal_regions,
|
||||
free_region_map: universal_regions.free_region_map,
|
||||
universal_regions,
|
||||
};
|
||||
|
||||
result.init_universal_regions(universal_regions);
|
||||
result.init_universal_regions();
|
||||
|
||||
result
|
||||
}
|
||||
@ -159,25 +166,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// R1 = { CFG, R0, R1 } // 'b
|
||||
///
|
||||
/// Here, R0 represents `'a`, and it contains (a) the entire CFG
|
||||
/// and (b) any free regions that it outlives, which in this case
|
||||
/// is just itself. R1 (`'b`) in contrast also outlives `'a` and
|
||||
/// hence contains R0 and R1.
|
||||
fn init_universal_regions(&mut self, universal_regions: &UniversalRegions<'tcx>) {
|
||||
let UniversalRegions {
|
||||
indices,
|
||||
free_region_map: _,
|
||||
} = universal_regions;
|
||||
/// and (b) any universally quantified regions that it outlives,
|
||||
/// which in this case is just itself. R1 (`'b`) in contrast also
|
||||
/// outlives `'a` and hence contains R0 and R1.
|
||||
fn init_universal_regions(&mut self) {
|
||||
// Update the names (if any)
|
||||
for (external_name, variable) in self.universal_regions.named_universal_regions() {
|
||||
self.definitions[variable].external_name = Some(external_name);
|
||||
}
|
||||
|
||||
// For each universally quantified region X:
|
||||
for (free_region, &variable) in indices {
|
||||
for variable in self.universal_regions.universal_regions() {
|
||||
// These should be free-region variables.
|
||||
assert!(match self.definitions[variable].origin {
|
||||
RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
// Initialize the name and a few other details.
|
||||
self.definitions[variable].name = Some(free_region);
|
||||
self.definitions[variable].is_universal = true;
|
||||
|
||||
// Add all nodes in the CFG to liveness constraints
|
||||
for (_location, point_index) in &self.point_indices {
|
||||
@ -196,6 +202,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
self.definitions.indices()
|
||||
}
|
||||
|
||||
/// Given a universal region in scope on the MIR, returns the
|
||||
/// corresponding index.
|
||||
///
|
||||
/// (Panics if `r` is not a registered universal region.)
|
||||
pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
|
||||
self.universal_regions.to_region_vid(r)
|
||||
}
|
||||
|
||||
/// Returns true if the region `r` contains the point `p`.
|
||||
///
|
||||
/// Panics if called before `solve()` executes,
|
||||
@ -237,19 +251,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
.as_ref()
|
||||
.expect("region values not yet inferred");
|
||||
|
||||
self.region_value_str_from_matrix(inferred_values, r)
|
||||
}
|
||||
|
||||
fn region_value_str_from_matrix(&self,
|
||||
matrix: &BitMatrix,
|
||||
r: RegionVid) -> String {
|
||||
let mut result = String::new();
|
||||
result.push_str("{");
|
||||
let mut sep = "";
|
||||
|
||||
for &point in self.point_indices.keys() {
|
||||
if self.region_contains_point_in_matrix(inferred_values, r, point) {
|
||||
if self.region_contains_point_in_matrix(matrix, r, point) {
|
||||
result.push_str(&format!("{}{:?}", sep, point));
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
|
||||
for fr in (0..self.num_universal_regions).map(RegionVid::new) {
|
||||
if self.region_contains_region_in_matrix(inferred_values, r, fr) {
|
||||
for fr in (0..self.universal_regions.len()).map(RegionVid::new) {
|
||||
if self.region_contains_region_in_matrix(matrix, r, fr) {
|
||||
result.push_str(&format!("{}{:?}", sep, fr));
|
||||
sep = ", ";
|
||||
}
|
||||
@ -289,8 +309,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
|
||||
/// Perform region inference.
|
||||
pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
|
||||
pub(super) fn solve(
|
||||
&mut self,
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
mir_def_id: DefId,
|
||||
) -> Option<ClosureRegionRequirements> {
|
||||
assert!(self.inferred_values.is_none(), "values already inferred");
|
||||
let tcx = infcx.tcx;
|
||||
|
||||
// Find the minimal regions that can solve the constraints. This is infallible.
|
||||
self.propagate_constraints(mir);
|
||||
@ -310,57 +336,135 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
// The universal regions are always found in a prefix of the
|
||||
// full list.
|
||||
let free_region_definitions = self.definitions
|
||||
let universal_definitions = self.definitions
|
||||
.iter_enumerated()
|
||||
.take_while(|(_, fr_definition)| fr_definition.name.is_some());
|
||||
.take_while(|(_, fr_definition)| fr_definition.is_universal);
|
||||
|
||||
for (fr, fr_definition) in free_region_definitions {
|
||||
self.check_free_region(infcx, fr, fr_definition);
|
||||
// Go through each of the universal regions `fr` and check that
|
||||
// they did not grow too large, accumulating any requirements
|
||||
// for our caller into the `outlives_requirements` vector.
|
||||
let mut outlives_requirements = vec![];
|
||||
for (fr, _) in universal_definitions {
|
||||
self.check_universal_region(infcx, fr, &mut outlives_requirements);
|
||||
}
|
||||
|
||||
// If this is not a closure, then there is no caller to which we can
|
||||
// "pass the buck". So if there are any outlives-requirements that were
|
||||
// not satisfied, we just have to report a hard error here.
|
||||
if !tcx.is_closure(mir_def_id) {
|
||||
for outlives_requirement in outlives_requirements {
|
||||
self.report_error(
|
||||
infcx,
|
||||
outlives_requirement.free_region,
|
||||
outlives_requirement.outlived_free_region,
|
||||
outlives_requirement.blame_span,
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
let num_external_vids = self.universal_regions.num_global_and_external_regions();
|
||||
|
||||
Some(ClosureRegionRequirements {
|
||||
num_external_vids,
|
||||
outlives_requirements,
|
||||
})
|
||||
}
|
||||
|
||||
fn check_free_region(
|
||||
/// Check the final value for the free region `fr` to see if it
|
||||
/// grew too large. In particular, examine what `end(X)` points
|
||||
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
|
||||
/// fr`, we want to check that `fr: X`. If not, that's either an
|
||||
/// error, or something we have to propagate to our creator.
|
||||
///
|
||||
/// Things that are to be propagated are accumulated into the
|
||||
/// `outlives_requirements` vector.
|
||||
fn check_universal_region(
|
||||
&self,
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
longer_fr: RegionVid,
|
||||
longer_definition: &RegionDefinition<'tcx>,
|
||||
outlives_requirements: &mut Vec<ClosureOutlivesRequirement>,
|
||||
) {
|
||||
let inferred_values = self.inferred_values.as_ref().unwrap();
|
||||
let longer_name = longer_definition.name.unwrap();
|
||||
let longer_value = inferred_values.iter(longer_fr.index());
|
||||
|
||||
// Find every region `shorter` such that `longer: shorter`
|
||||
// (because `longer` includes `end(shorter)`).
|
||||
for shorter_fr in longer_value.take_while(|&i| i < self.num_universal_regions) {
|
||||
let shorter_fr = RegionVid::new(shorter_fr);
|
||||
debug!("check_universal_region(fr={:?})", longer_fr);
|
||||
|
||||
// `fr` includes `end(fr)`, that's not especially
|
||||
// interesting.
|
||||
if longer_fr == shorter_fr {
|
||||
// Find every region `o` such that `fr: o`
|
||||
// (because `fr` includes `end(o)`).
|
||||
let shorter_frs = longer_value
|
||||
.take_while(|&i| i < self.universal_regions.len())
|
||||
.map(RegionVid::new);
|
||||
for shorter_fr in shorter_frs {
|
||||
// If it is known that `fr: o`, carry on.
|
||||
if self.universal_regions.outlives(longer_fr, shorter_fr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shorter_definition = &self.definitions[shorter_fr];
|
||||
let shorter_name = shorter_definition.name.unwrap();
|
||||
debug!(
|
||||
"check_universal_region: fr={:?} does not outlive shorter_fr={:?}",
|
||||
longer_fr,
|
||||
shorter_fr,
|
||||
);
|
||||
|
||||
// Check that `o <= fr`. If not, report an error.
|
||||
if !self.free_region_map
|
||||
.sub_free_regions(shorter_name, longer_name)
|
||||
{
|
||||
// FIXME: worst error msg ever
|
||||
let blame_span = self.blame_span(longer_fr, shorter_fr);
|
||||
infcx.tcx.sess.span_err(
|
||||
blame_span,
|
||||
&format!(
|
||||
"free region `{}` does not outlive `{}`",
|
||||
longer_name,
|
||||
shorter_name
|
||||
),
|
||||
let blame_span = self.blame_span(longer_fr, shorter_fr);
|
||||
|
||||
// Shrink `fr` until we find a non-local region (if we do).
|
||||
// We'll call that `fr-` -- it's ever so slightly smaller than `fr`.
|
||||
if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) {
|
||||
debug!("check_universal_region: fr_minus={:?}", fr_minus);
|
||||
|
||||
// Grow `shorter_fr` until we find a non-local
|
||||
// regon. (We always will.) We'll call that
|
||||
// `shorter_fr+` -- it's ever so slightly larger than
|
||||
// `fr`.
|
||||
let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr);
|
||||
debug!(
|
||||
"check_universal_region: shorter_fr_plus={:?}",
|
||||
shorter_fr_plus
|
||||
);
|
||||
|
||||
// Push the constraint `fr-: shorter_fr+`
|
||||
outlives_requirements.push(ClosureOutlivesRequirement {
|
||||
free_region: fr_minus,
|
||||
outlived_free_region: shorter_fr_plus,
|
||||
blame_span: blame_span,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If we could not shrink `fr` to something smaller that
|
||||
// the external users care about, then we can't pass the
|
||||
// buck; just report an error.
|
||||
self.report_error(infcx, longer_fr, shorter_fr, blame_span);
|
||||
}
|
||||
}
|
||||
|
||||
fn report_error(
|
||||
&self,
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
fr: RegionVid,
|
||||
outlived_fr: RegionVid,
|
||||
blame_span: Span,
|
||||
) {
|
||||
// Obviously uncool error reporting.
|
||||
|
||||
let fr_string = match self.definitions[fr].external_name {
|
||||
Some(r) => format!("free region `{}`", r),
|
||||
None => format!("free region `{:?}`", fr),
|
||||
};
|
||||
|
||||
let outlived_fr_string = match self.definitions[outlived_fr].external_name {
|
||||
Some(r) => format!("free region `{}`", r),
|
||||
None => format!("free region `{:?}`", outlived_fr),
|
||||
};
|
||||
|
||||
infcx.tcx.sess.span_err(
|
||||
blame_span,
|
||||
&format!("{} does not outlive {}", fr_string, outlived_fr_string,),
|
||||
);
|
||||
}
|
||||
|
||||
/// Propagate the region constraints: this will grow the values
|
||||
/// for each region variable until all the constraints are
|
||||
/// satisfied. Note that some values may grow **too** large to be
|
||||
@ -421,8 +525,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
stack.push(start_point);
|
||||
while let Some(p) = stack.pop() {
|
||||
debug!(" copy: p={:?}", p);
|
||||
|
||||
if !self.region_contains_point_in_matrix(inferred_values, from_region, p) {
|
||||
debug!(" not in from-region");
|
||||
continue;
|
||||
@ -464,7 +566,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// and make sure they are included in the `to_region`.
|
||||
let universal_region_indices = inferred_values
|
||||
.iter(from_region.index())
|
||||
.take_while(|&i| i < self.num_universal_regions)
|
||||
.take_while(|&i| i < self.universal_regions.len())
|
||||
.collect::<Vec<_>>();
|
||||
for fr in &universal_region_indices {
|
||||
changed |= inferred_values.add(to_region.index(), *fr);
|
||||
@ -535,7 +637,11 @@ impl<'tcx> RegionDefinition<'tcx> {
|
||||
// Create a new region definition. Note that, for free
|
||||
// regions, these fields get updated later in
|
||||
// `init_universal_regions`.
|
||||
Self { origin, name: None }
|
||||
Self {
|
||||
origin,
|
||||
is_universal: false,
|
||||
external_name: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,3 +657,70 @@ impl fmt::Debug for Constraint {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ClosureRegionRequirementsExt {
|
||||
fn apply_requirements<'tcx>(
|
||||
&self,
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
location: Location,
|
||||
closure_def_id: DefId,
|
||||
closure_substs: ty::ClosureSubsts<'tcx>,
|
||||
);
|
||||
}
|
||||
|
||||
impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
|
||||
/// Given an instance T of the closure type, this method
|
||||
/// instantiates the "extra" requirements that we computed for the
|
||||
/// closure into the inference context. This has the effect of
|
||||
/// adding new subregion obligations to existing variables.
|
||||
///
|
||||
/// As described on `ClosureRegionRequirements`, the extra
|
||||
/// requirements are expressed in terms of regionvids that index
|
||||
/// into the free regions that appear on the closure type. So, to
|
||||
/// do this, we first copy those regions out from the type T into
|
||||
/// a vector. Then we can just index into that vector to extract
|
||||
/// out the corresponding region from T and apply the
|
||||
/// requirements.
|
||||
fn apply_requirements<'tcx>(
|
||||
&self,
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
location: Location,
|
||||
closure_def_id: DefId,
|
||||
closure_substs: ty::ClosureSubsts<'tcx>,
|
||||
) {
|
||||
let tcx = infcx.tcx;
|
||||
|
||||
debug!(
|
||||
"apply_requirements(location={:?}, closure_def_id={:?}, closure_substs={:?})",
|
||||
location,
|
||||
closure_def_id,
|
||||
closure_substs
|
||||
);
|
||||
|
||||
// Get Tu.
|
||||
let user_closure_ty = tcx.mk_closure(closure_def_id, closure_substs);
|
||||
debug!("apply_requirements: user_closure_ty={:?}", user_closure_ty);
|
||||
|
||||
// Extract the values of the free regions in `user_closure_ty`
|
||||
// into a vector. These are the regions that we will be
|
||||
// relating to one another.
|
||||
let closure_mapping =
|
||||
UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids);
|
||||
debug!("apply_requirements: closure_mapping={:?}", closure_mapping);
|
||||
|
||||
// Create the predicates.
|
||||
for outlives_requirement in &self.outlives_requirements {
|
||||
let region = closure_mapping[outlives_requirement.free_region];
|
||||
let outlived_region = closure_mapping[outlives_requirement.outlived_free_region];
|
||||
debug!(
|
||||
"apply_requirements: region={:?} outlived_region={:?} outlives_requirements={:?}",
|
||||
region,
|
||||
outlived_region,
|
||||
outlives_requirement
|
||||
);
|
||||
// FIXME, this origin is not entirely suitable.
|
||||
let origin = SubregionOrigin::CallRcvr(outlives_requirement.blame_span);
|
||||
infcx.sub_regions(origin, outlived_region, region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,11 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
|
||||
use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable};
|
||||
use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
|
||||
use rustc::mir::RETURN_PLACE;
|
||||
use rustc::mir::visit::{MutVisitor, TyContext};
|
||||
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
|
||||
|
||||
@ -25,25 +26,24 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
mir: &mut Mir<'tcx>,
|
||||
) {
|
||||
// Create inference variables for each of the free regions
|
||||
// declared on the function signature.
|
||||
let free_region_inference_vars = (0..universal_regions.indices.len())
|
||||
.map(RegionVid::new)
|
||||
.map(|vid_expected| {
|
||||
let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
|
||||
assert_eq!(vid_expected, r.to_region_vid());
|
||||
r
|
||||
})
|
||||
.collect();
|
||||
|
||||
debug!("renumber_mir()");
|
||||
debug!("renumber_mir: universal_regions={:#?}", universal_regions);
|
||||
debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
|
||||
|
||||
// Update the return type and types of the arguments based on the
|
||||
// `universal_regions` computation.
|
||||
debug!("renumber_mir: output_ty={:?}", universal_regions.output_ty);
|
||||
mir.local_decls[RETURN_PLACE].ty = universal_regions.output_ty;
|
||||
for (&input_ty, local) in universal_regions
|
||||
.input_tys
|
||||
.iter()
|
||||
.zip((1..).map(Local::new))
|
||||
{
|
||||
debug!("renumber_mir: input_ty={:?} local={:?}", input_ty, local);
|
||||
mir.local_decls[local].ty = input_ty;
|
||||
}
|
||||
|
||||
let mut visitor = NLLVisitor {
|
||||
infcx,
|
||||
universal_regions,
|
||||
free_region_inference_vars,
|
||||
arg_count: mir.arg_count,
|
||||
};
|
||||
visitor.visit_mir(mir);
|
||||
@ -51,8 +51,6 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
|
||||
|
||||
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
universal_regions: &'a UniversalRegions<'tcx>,
|
||||
free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
|
||||
arg_count: usize,
|
||||
}
|
||||
|
||||
@ -74,20 +72,17 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Renumbers the regions appearing in `value`, but those regions
|
||||
/// are expected to be free regions from the function signature.
|
||||
fn renumber_universal_regions<T>(&mut self, value: &T) -> T
|
||||
/// Checks that all the regions appearing in `value` have already
|
||||
/// been renumbered. `FreeRegions` code should have done this.
|
||||
fn assert_free_regions_are_renumbered<T>(&self, value: &T)
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
debug!("renumber_universal_regions(value={:?})", value);
|
||||
debug!("assert_free_regions_are_renumbered(value={:?})", value);
|
||||
|
||||
self.infcx
|
||||
.tcx
|
||||
.fold_regions(value, &mut false, |region, _depth| {
|
||||
let index = self.universal_regions.indices[®ion];
|
||||
self.free_region_inference_vars[index]
|
||||
})
|
||||
self.infcx.tcx.for_each_free_region(value, |region| {
|
||||
region.to_region_vid(); // will panic if `region` is not renumbered
|
||||
});
|
||||
}
|
||||
|
||||
fn is_argument_or_return_slot(&self, local: Local) -> bool {
|
||||
@ -110,12 +105,12 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
|
||||
ty_context
|
||||
);
|
||||
|
||||
let old_ty = *ty;
|
||||
*ty = if is_arg {
|
||||
self.renumber_universal_regions(&old_ty)
|
||||
if is_arg {
|
||||
self.assert_free_regions_are_renumbered(ty);
|
||||
} else {
|
||||
self.renumber_regions(ty_context, &old_ty)
|
||||
};
|
||||
*ty = self.renumber_regions(ty_context, ty);
|
||||
}
|
||||
|
||||
debug!("visit_ty: ty={:?}", ty);
|
||||
}
|
||||
|
||||
@ -138,6 +133,11 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
|
||||
debug!("visit_region: region={:?}", region);
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, location: Location) {
|
||||
let ty_context = TyContext::Location(location);
|
||||
*constant = self.renumber_regions(ty_context, &*constant);
|
||||
}
|
||||
|
||||
fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
|
||||
debug!(
|
||||
"visit_closure_substs(substs={:?}, location={:?})",
|
||||
|
@ -15,7 +15,6 @@ use rustc::ty;
|
||||
use transform::type_check::MirTypeckRegionConstraints;
|
||||
use transform::type_check::OutlivesSet;
|
||||
|
||||
use super::universal_regions::UniversalRegions;
|
||||
use super::region_infer::RegionInferenceContext;
|
||||
|
||||
/// When the MIR type-checker executes, it validates all the types in
|
||||
@ -25,20 +24,17 @@ use super::region_infer::RegionInferenceContext;
|
||||
/// them into the NLL `RegionInferenceContext`.
|
||||
pub(super) fn generate<'tcx>(
|
||||
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
) {
|
||||
SubtypeConstraintGenerator {
|
||||
regioncx,
|
||||
universal_regions,
|
||||
mir,
|
||||
}.generate(constraints);
|
||||
}
|
||||
|
||||
struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
|
||||
regioncx: &'cx mut RegionInferenceContext<'tcx>,
|
||||
universal_regions: &'cx UniversalRegions<'tcx>,
|
||||
mir: &'cx Mir<'tcx>,
|
||||
}
|
||||
|
||||
@ -106,7 +102,7 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
|
||||
if let ty::ReVar(vid) = r {
|
||||
*vid
|
||||
} else {
|
||||
self.universal_regions.indices[&r]
|
||||
self.regioncx.to_region_vid(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,69 +22,681 @@
|
||||
//! The code in this file doesn't *do anything* with those results; it
|
||||
//! just returns them for other code to use.
|
||||
|
||||
use rustc::hir::HirId;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::infer::outlives::free_region_map::FreeRegionMap;
|
||||
use rustc::ty::{self, RegionVid};
|
||||
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
|
||||
use rustc::infer::region_constraints::GenericKind;
|
||||
use rustc::infer::outlives::bounds::{self, OutlivesBound};
|
||||
use rustc::ty::{self, RegionVid, Ty, TyCtxt};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::util::nodemap::FxHashMap;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||
use rustc_data_structures::transitive_relation::TransitiveRelation;
|
||||
use std::iter;
|
||||
use syntax::ast;
|
||||
|
||||
use super::ToRegionVid;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UniversalRegions<'tcx> {
|
||||
/// Given a universally quantified region defined on this function
|
||||
/// (either early- or late-bound), this maps it to its internal
|
||||
/// region index. When the region context is created, the first N
|
||||
/// variables will be created based on these indices.
|
||||
pub indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
|
||||
indices: UniversalRegionIndices<'tcx>,
|
||||
|
||||
/// The map from the typeck tables telling us how to relate universal regions.
|
||||
pub free_region_map: &'tcx FreeRegionMap<'tcx>,
|
||||
/// The vid assigned to `'static`
|
||||
pub fr_static: RegionVid,
|
||||
|
||||
/// We create region variables such that they are ordered by their
|
||||
/// `RegionClassification`. The first block are globals, then
|
||||
/// externals, then locals. So things from:
|
||||
/// - `FIRST_GLOBAL_INDEX..first_extern_index` are global;
|
||||
/// - `first_extern_index..first_local_index` are external; and
|
||||
/// - first_local_index..num_universals` are local.
|
||||
first_extern_index: usize,
|
||||
|
||||
/// See `first_extern_index`.
|
||||
first_local_index: usize,
|
||||
|
||||
/// The total number of universal region variables instantiated.
|
||||
num_universals: usize,
|
||||
|
||||
/// The "defining" type for this function, with all universal
|
||||
/// regions instantiated. For a closure or generator, this is the
|
||||
/// closure type, but for a top-level function it's the `TyFnDef`.
|
||||
pub defining_ty: Ty<'tcx>,
|
||||
|
||||
/// The return type of this function, with all regions replaced
|
||||
/// by their universal `RegionVid` equivalents.
|
||||
pub output_ty: Ty<'tcx>,
|
||||
|
||||
/// The fully liberated input types of this function, with all
|
||||
/// regions replaced by their universal `RegionVid` equivalents.
|
||||
pub input_tys: &'tcx [Ty<'tcx>],
|
||||
|
||||
/// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to
|
||||
/// be true. These encode relationships like `T: 'a` that are
|
||||
/// added via implicit bounds.
|
||||
///
|
||||
/// Each region here is guaranteed to be a key in the `indices`
|
||||
/// map. We use the "original" regions (i.e., the keys from the
|
||||
/// map, and not the values) because the code in
|
||||
/// `process_registered_region_obligations` has some special-cased
|
||||
/// logic expecting to see (e.g.) `ReStatic`, and if we supplied
|
||||
/// our special inference variable there, we would mess that up.
|
||||
pub region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
|
||||
|
||||
relations: UniversalRegionRelations,
|
||||
}
|
||||
|
||||
pub fn universal_regions<'a, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
item_def_id: DefId,
|
||||
) -> UniversalRegions<'tcx> {
|
||||
debug!("universal_regions(item_def_id={:?})", item_def_id);
|
||||
#[derive(Debug)]
|
||||
struct UniversalRegionIndices<'tcx> {
|
||||
/// For those regions that may appear in the parameter environment
|
||||
/// ('static and early-bound regions), we maintain a map from the
|
||||
/// `ty::Region` to the internal `RegionVid` we are using. This is
|
||||
/// used because trait matching and type-checking will feed us
|
||||
/// region constraints that reference those regions and we need to
|
||||
/// be able to map them our internal `RegionVid`. This is
|
||||
/// basically equivalent to a `Substs`, except that it also
|
||||
/// contains an entry for `ReStatic` -- it might be nice to just
|
||||
/// use a substs, and then handle `ReStatic` another way.
|
||||
indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
|
||||
}
|
||||
|
||||
let mut indices = FxHashMap();
|
||||
#[derive(Debug)]
|
||||
struct UniversalRegionRelations {
|
||||
/// Stores the outlives relations that are known to hold from the
|
||||
/// implied bounds, in-scope where clauses, and that sort of
|
||||
/// thing.
|
||||
outlives: TransitiveRelation<RegionVid>,
|
||||
|
||||
// `'static` is always free.
|
||||
insert_free_region(&mut indices, infcx.tcx.types.re_static);
|
||||
/// This is the `<=` relation; that is, if `a: b`, then `b <= a`,
|
||||
/// and we store that here. This is useful when figuring out how
|
||||
/// to express some local region in terms of external regions our
|
||||
/// caller will understand.
|
||||
inverse_outlives: TransitiveRelation<RegionVid>,
|
||||
}
|
||||
|
||||
// Extract the early regions.
|
||||
let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
|
||||
for item_subst in item_substs {
|
||||
if let Some(region) = item_subst.as_region() {
|
||||
insert_free_region(&mut indices, region);
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum RegionClassification {
|
||||
/// A **global** region is one that can be named from
|
||||
/// anywhere. There is only one, `'static`.
|
||||
Global,
|
||||
|
||||
/// An **external** region is only relevant for closures. In that
|
||||
/// case, it refers to regions that are free in the closure type
|
||||
/// -- basically, something bound in the surrounding context.
|
||||
///
|
||||
/// Consider this example:
|
||||
///
|
||||
/// ```
|
||||
/// fn foo<'a, 'b>(a: &'a u32, b: &'b u32, c: &'static u32) {
|
||||
/// let closure = for<'x> |x: &'x u32| { .. };
|
||||
/// ^^^^^^^ pretend this were legal syntax
|
||||
/// for declaring a late-bound region in
|
||||
/// a closure signature
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here, the lifetimes `'a` and `'b` would be **external** to the
|
||||
/// closure.
|
||||
///
|
||||
/// If we are not analyzing a closure, there are no external
|
||||
/// lifetimes.
|
||||
External,
|
||||
|
||||
/// A **local** lifetime is one about which we know the full set
|
||||
/// of relevant constraints (that is, relationships to other named
|
||||
/// regions). For a closure, this includes any region bound in
|
||||
/// the closure's signature. For a fn item, this includes all
|
||||
/// regions other than global ones.
|
||||
///
|
||||
/// Continuing with the example from `External`, if we were
|
||||
/// analyzing the closure, then `'x` would be local (and `'a` and
|
||||
/// `'b` are external). If we are analyzing the function item
|
||||
/// `foo`, then `'a` and `'b` are local (and `'x` is not in
|
||||
/// scope).
|
||||
Local,
|
||||
}
|
||||
|
||||
const FIRST_GLOBAL_INDEX: usize = 0;
|
||||
|
||||
impl<'tcx> UniversalRegions<'tcx> {
|
||||
/// Creates a new and fully initialized `UniversalRegions` that
|
||||
/// contains indices for all the free regions found in the given
|
||||
/// MIR -- that is, all the regions that appear in the function's
|
||||
/// signature. This will also compute the relationships that are
|
||||
/// known between those regions.
|
||||
pub fn new(
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
mir_def_id: DefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Self {
|
||||
let tcx = infcx.tcx;
|
||||
let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).unwrap();
|
||||
let mir_hir_id = tcx.hir.node_to_hir_id(mir_node_id);
|
||||
UniversalRegionsBuilder {
|
||||
infcx,
|
||||
mir_def_id,
|
||||
mir_node_id,
|
||||
mir_hir_id,
|
||||
param_env,
|
||||
region_bound_pairs: vec![],
|
||||
relations: UniversalRegionRelations {
|
||||
outlives: TransitiveRelation::new(),
|
||||
inverse_outlives: TransitiveRelation::new(),
|
||||
},
|
||||
}.build()
|
||||
}
|
||||
|
||||
/// Given a reference to a closure type, extracts all the values
|
||||
/// from its free regions and returns a vector with them. This is
|
||||
/// used when the closure's creator checks that the
|
||||
/// `ClosureRegionRequirements` are met. The requirements from
|
||||
/// `ClosureRegionRequirements` are expressed in terms of
|
||||
/// `RegionVid` entries that map into the returned vector `V`: so
|
||||
/// if the `ClosureRegionRequirements` contains something like
|
||||
/// `'1: '2`, then the caller would impose the constraint that
|
||||
/// `V[1]: V[2]`.
|
||||
pub fn closure_mapping(
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
closure_ty: Ty<'tcx>,
|
||||
expected_num_vars: usize,
|
||||
) -> IndexVec<RegionVid, ty::Region<'tcx>> {
|
||||
let mut region_mapping = IndexVec::with_capacity(expected_num_vars);
|
||||
region_mapping.push(infcx.tcx.types.re_static);
|
||||
infcx.tcx.for_each_free_region(&closure_ty, |fr| {
|
||||
region_mapping.push(fr);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
region_mapping.len(),
|
||||
expected_num_vars,
|
||||
"index vec had unexpected number of variables"
|
||||
);
|
||||
|
||||
region_mapping
|
||||
}
|
||||
|
||||
/// True if `r` is a member of this set of universal regions.
|
||||
pub fn is_universal_region(&self, r: RegionVid) -> bool {
|
||||
(FIRST_GLOBAL_INDEX..self.num_universals).contains(r.index())
|
||||
}
|
||||
|
||||
/// Classifies `r` as a universal region, returning `None` if this
|
||||
/// is not a member of this set of universal regions.
|
||||
pub fn region_classification(&self, r: RegionVid) -> Option<RegionClassification> {
|
||||
let index = r.index();
|
||||
if (FIRST_GLOBAL_INDEX..self.first_extern_index).contains(index) {
|
||||
Some(RegionClassification::Global)
|
||||
} else if (self.first_extern_index..self.first_local_index).contains(index) {
|
||||
Some(RegionClassification::External)
|
||||
} else if (self.first_local_index..self.num_universals).contains(index) {
|
||||
Some(RegionClassification::Local)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the late-bound regions. Use the liberated fn sigs,
|
||||
// where the late-bound regions will have been converted into free
|
||||
// regions, and add them to the map.
|
||||
let item_id = infcx.tcx.hir.as_local_node_id(item_def_id).unwrap();
|
||||
let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
|
||||
let tables = infcx.tcx.typeck_tables_of(item_def_id);
|
||||
let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
|
||||
infcx
|
||||
.tcx
|
||||
.for_each_free_region(&fn_sig.inputs_and_output, |region| {
|
||||
if let ty::ReFree(_) = *region {
|
||||
insert_free_region(&mut indices, region);
|
||||
/// Returns an iterator over all the RegionVids corresponding to
|
||||
/// universally quantified free regions.
|
||||
pub fn universal_regions(&self) -> impl Iterator<Item = RegionVid> {
|
||||
(FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new)
|
||||
}
|
||||
|
||||
/// True if `r` is classied as a global region.
|
||||
pub fn is_global_free_region(&self, r: RegionVid) -> bool {
|
||||
self.region_classification(r) == Some(RegionClassification::Global)
|
||||
}
|
||||
|
||||
/// True if `r` is classied as an external region.
|
||||
pub fn is_extern_free_region(&self, r: RegionVid) -> bool {
|
||||
self.region_classification(r) == Some(RegionClassification::External)
|
||||
}
|
||||
|
||||
/// True if `r` is classied as an local region.
|
||||
pub fn is_local_free_region(&self, r: RegionVid) -> bool {
|
||||
self.region_classification(r) == Some(RegionClassification::Local)
|
||||
}
|
||||
|
||||
/// Returns the number of universal regions created in any category.
|
||||
pub fn len(&self) -> usize {
|
||||
self.num_universals
|
||||
}
|
||||
|
||||
/// Finds an "upper bound" for `fr` that is not local. In other
|
||||
/// words, returns the smallest (*) known region `fr1` that (a)
|
||||
/// outlives `fr` and (b) is not local. This cannot fail, because
|
||||
/// we will always find `'static` at worst.
|
||||
///
|
||||
/// (*) If there are multiple competing choices, we pick the "postdominating"
|
||||
/// one. See `TransitiveRelation::postdom_upper_bound` for details.
|
||||
pub fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid {
|
||||
debug!("non_local_upper_bound(fr={:?})", fr);
|
||||
self.non_local_bound(&self.relations.inverse_outlives, fr)
|
||||
.unwrap_or(self.fr_static)
|
||||
}
|
||||
|
||||
/// Finds a "lower bound" for `fr` that is not local. In other
|
||||
/// words, returns the largest (*) known region `fr1` that (a) is
|
||||
/// outlived by `fr` and (b) is not local. This cannot fail,
|
||||
/// because we will always find `'static` at worst.
|
||||
///
|
||||
/// (*) If there are multiple competing choices, we pick the "postdominating"
|
||||
/// one. See `TransitiveRelation::postdom_upper_bound` for details.
|
||||
pub fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
|
||||
debug!("non_local_lower_bound(fr={:?})", fr);
|
||||
self.non_local_bound(&self.relations.outlives, fr)
|
||||
}
|
||||
|
||||
/// Returns the number of global plus external universal regions.
|
||||
/// For closures, these are the regions that appear free in the
|
||||
/// closure type (versus those bound in the closure
|
||||
/// signature). They are therefore the regions between which the
|
||||
/// closure may impose constraints that its creator must verify.
|
||||
pub fn num_global_and_external_regions(&self) -> usize {
|
||||
self.first_local_index
|
||||
}
|
||||
|
||||
/// Helper for `non_local_upper_bound` and
|
||||
/// `non_local_lower_bound`. Repeatedly invokes `postdom_parent`
|
||||
/// until we find something that is not local. Returns None if we
|
||||
/// never do so.
|
||||
fn non_local_bound(
|
||||
&self,
|
||||
relation: &TransitiveRelation<RegionVid>,
|
||||
fr0: RegionVid,
|
||||
) -> Option<RegionVid> {
|
||||
let mut external_parents = vec![];
|
||||
let mut queue = vec![&fr0];
|
||||
|
||||
// Keep expanding `fr` into its parents until we reach
|
||||
// non-local regions.
|
||||
while let Some(fr) = queue.pop() {
|
||||
if !self.is_local_free_region(*fr) {
|
||||
external_parents.push(fr);
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
debug!("universal_regions: indices={:#?}", indices);
|
||||
queue.extend(relation.parents(fr));
|
||||
}
|
||||
|
||||
UniversalRegions { indices, free_region_map: &tables.free_region_map }
|
||||
debug!("non_local_bound: external_parents={:?}", external_parents);
|
||||
|
||||
// In case we find more than one, reduce to one for
|
||||
// convenience. This is to prevent us from generating more
|
||||
// complex constraints, but it will cause spurious errors.
|
||||
let post_dom = relation
|
||||
.mutual_immediate_postdominator(external_parents)
|
||||
.cloned();
|
||||
|
||||
debug!("non_local_bound: post_dom={:?}", post_dom);
|
||||
|
||||
post_dom.and_then(|post_dom| {
|
||||
// If the mutual immediate postdom is not local, then
|
||||
// there is no non-local result we can return.
|
||||
if !self.is_local_free_region(post_dom) {
|
||||
Some(post_dom)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// True if fr1 is known to outlive fr2.
|
||||
///
|
||||
/// This will only ever be true for universally quantified regions.
|
||||
pub fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
|
||||
self.relations.outlives.contains(&fr1, &fr2)
|
||||
}
|
||||
|
||||
/// Returns a vector of free regions `x` such that `fr1: x` is
|
||||
/// known to hold.
|
||||
pub fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<&RegionVid> {
|
||||
self.relations.outlives.reachable_from(&fr1)
|
||||
}
|
||||
|
||||
/// Get an iterator over all the early-bound regions that have names.
|
||||
pub fn named_universal_regions<'s>(
|
||||
&'s self,
|
||||
) -> impl Iterator<Item = (ty::Region<'tcx>, ty::RegionVid)> + 's {
|
||||
self.indices.indices.iter().map(|(&r, &v)| (r, v))
|
||||
}
|
||||
|
||||
/// See `UniversalRegionIndices::to_region_vid`.
|
||||
pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
|
||||
self.indices.to_region_vid(r)
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_free_region<'tcx>(
|
||||
universal_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
|
||||
region: ty::Region<'tcx>,
|
||||
) {
|
||||
let next = RegionVid::new(universal_regions.len());
|
||||
universal_regions.entry(region).or_insert(next);
|
||||
struct UniversalRegionsBuilder<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
|
||||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
mir_def_id: DefId,
|
||||
mir_hir_id: HirId,
|
||||
mir_node_id: ast::NodeId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
|
||||
relations: UniversalRegionRelations,
|
||||
}
|
||||
|
||||
const FR: NLLRegionVariableOrigin = NLLRegionVariableOrigin::FreeRegion;
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
|
||||
fn build(mut self) -> UniversalRegions<'tcx> {
|
||||
debug!("build(mir_def_id={:?})", self.mir_def_id);
|
||||
|
||||
let param_env = self.param_env;
|
||||
debug!("build: param_env={:?}", param_env);
|
||||
|
||||
assert_eq!(FIRST_GLOBAL_INDEX, self.infcx.num_region_vars());
|
||||
|
||||
// Create the "global" region that is always free in all contexts: 'static.
|
||||
let fr_static = self.infcx.next_nll_region_var(FR).to_region_vid();
|
||||
|
||||
// We've now added all the global regions. The next ones we
|
||||
// add will be external.
|
||||
let first_extern_index = self.infcx.num_region_vars();
|
||||
|
||||
let defining_ty = self.defining_ty();
|
||||
debug!("build: defining_ty={:?}", defining_ty);
|
||||
|
||||
let indices = self.compute_indices(fr_static, defining_ty);
|
||||
debug!("build: indices={:?}", indices);
|
||||
|
||||
let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
|
||||
|
||||
// "Liberate" the late-bound regions. These correspond to
|
||||
// "local" free regions.
|
||||
let first_local_index = self.infcx.num_region_vars();
|
||||
let inputs_and_output = self.infcx
|
||||
.replace_bound_regions_with_nll_infer_vars(FR, &bound_inputs_and_output);
|
||||
let num_universals = self.infcx.num_region_vars();
|
||||
|
||||
// Insert the facts we know from the predicates. Why? Why not.
|
||||
self.add_outlives_bounds(&indices, bounds::explicit_outlives_bounds(param_env));
|
||||
|
||||
// Add the implied bounds from inputs and outputs.
|
||||
for ty in inputs_and_output {
|
||||
debug!("build: input_or_output={:?}", ty);
|
||||
self.add_implied_bounds(&indices, ty);
|
||||
}
|
||||
|
||||
// Finally, outlives is reflexive, and static outlives every
|
||||
// other free region.
|
||||
for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) {
|
||||
debug!("build: relating free region {:?} to itself and to 'static", fr);
|
||||
self.relations.relate_universal_regions(fr, fr);
|
||||
self.relations.relate_universal_regions(fr_static, fr);
|
||||
}
|
||||
|
||||
let (output_ty, input_tys) = inputs_and_output.split_last().unwrap();
|
||||
|
||||
// we should not have created any more variables
|
||||
assert_eq!(self.infcx.num_region_vars(), num_universals);
|
||||
|
||||
debug!("build: global regions = {}..{}",
|
||||
FIRST_GLOBAL_INDEX,
|
||||
first_extern_index);
|
||||
debug!("build: extern regions = {}..{}",
|
||||
first_extern_index,
|
||||
first_local_index);
|
||||
debug!("build: local regions = {}..{}",
|
||||
first_local_index,
|
||||
num_universals);
|
||||
|
||||
UniversalRegions {
|
||||
indices,
|
||||
fr_static,
|
||||
first_extern_index,
|
||||
first_local_index,
|
||||
num_universals,
|
||||
defining_ty,
|
||||
output_ty,
|
||||
input_tys,
|
||||
region_bound_pairs: self.region_bound_pairs,
|
||||
relations: self.relations,
|
||||
}
|
||||
}
|
||||
|
||||
fn defining_ty(&self) -> ty::Ty<'tcx> {
|
||||
let tcx = self.infcx.tcx;
|
||||
let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
|
||||
|
||||
let defining_ty = if self.mir_def_id == closure_base_def_id {
|
||||
tcx.type_of(closure_base_def_id)
|
||||
} else {
|
||||
let tables = tcx.typeck_tables_of(self.mir_def_id);
|
||||
tables.node_id_to_type(self.mir_hir_id)
|
||||
};
|
||||
|
||||
self.infcx
|
||||
.replace_free_regions_with_nll_infer_vars(FR, &defining_ty)
|
||||
}
|
||||
|
||||
fn compute_indices(
|
||||
&self,
|
||||
fr_static: RegionVid,
|
||||
defining_ty: Ty<'tcx>,
|
||||
) -> UniversalRegionIndices<'tcx> {
|
||||
let tcx = self.infcx.tcx;
|
||||
let gcx = tcx.global_tcx();
|
||||
let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
|
||||
let identity_substs = Substs::identity_for_item(gcx, closure_base_def_id);
|
||||
let fr_substs = match defining_ty.sty {
|
||||
ty::TyClosure(_, substs) | ty::TyGenerator(_, substs, ..) => {
|
||||
// In the case of closures, we rely on the fact that
|
||||
// the first N elements in the ClosureSubsts are
|
||||
// inherited from the `closure_base_def_id`.
|
||||
// Therefore, when we zip together (below) with
|
||||
// `identity_substs`, we will get only those regions
|
||||
// that correspond to early-bound regions declared on
|
||||
// the `closure_base_def_id`.
|
||||
assert!(substs.substs.len() >= identity_substs.len());
|
||||
substs.substs
|
||||
}
|
||||
ty::TyFnDef(_, substs) => substs,
|
||||
_ => bug!(),
|
||||
};
|
||||
|
||||
let global_mapping = iter::once((gcx.types.re_static, fr_static));
|
||||
let subst_mapping = identity_substs
|
||||
.regions()
|
||||
.zip(fr_substs.regions().map(|r| r.to_region_vid()));
|
||||
|
||||
UniversalRegionIndices {
|
||||
indices: global_mapping.chain(subst_mapping).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_inputs_and_output(
|
||||
&self,
|
||||
indices: &UniversalRegionIndices<'tcx>,
|
||||
defining_ty: Ty<'tcx>,
|
||||
) -> ty::Binder<&'tcx ty::Slice<Ty<'tcx>>> {
|
||||
let tcx = self.infcx.tcx;
|
||||
match defining_ty.sty {
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
assert_eq!(self.mir_def_id, def_id);
|
||||
let closure_sig = substs.closure_sig_ty(def_id, tcx).fn_sig(tcx);
|
||||
let inputs_and_output = closure_sig.inputs_and_output();
|
||||
let closure_ty = tcx.closure_env_ty(def_id, substs).unwrap();
|
||||
ty::Binder::fuse(
|
||||
closure_ty,
|
||||
inputs_and_output,
|
||||
|closure_ty, inputs_and_output| {
|
||||
// The "inputs" of the closure in the
|
||||
// signature appear as a tuple. The MIR side
|
||||
// flattens this tuple.
|
||||
let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap();
|
||||
assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs");
|
||||
let inputs = match tuplized_inputs[0].sty {
|
||||
ty::TyTuple(inputs, _) => inputs,
|
||||
_ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]),
|
||||
};
|
||||
|
||||
tcx.mk_type_list(
|
||||
iter::once(closure_ty)
|
||||
.chain(inputs.iter().cloned())
|
||||
.chain(iter::once(output)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
ty::TyGenerator(def_id, substs, ..) => {
|
||||
assert_eq!(self.mir_def_id, def_id);
|
||||
let output = substs.generator_return_ty(def_id, tcx);
|
||||
let inputs_and_output = self.infcx.tcx.intern_type_list(&[defining_ty, output]);
|
||||
ty::Binder::dummy(inputs_and_output)
|
||||
}
|
||||
|
||||
ty::TyFnDef(def_id, _) => {
|
||||
let sig = tcx.fn_sig(def_id);
|
||||
let sig = indices.fold_to_region_vids(tcx, &sig);
|
||||
return sig.inputs_and_output();
|
||||
}
|
||||
|
||||
_ => span_bug!(
|
||||
tcx.def_span(self.mir_def_id),
|
||||
"unexpected defining type: {:?}",
|
||||
defining_ty
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the type of a single local, which should represent
|
||||
/// either the return type of the MIR or one of its arguments. At
|
||||
/// the same time, compute and add any implied bounds that come
|
||||
/// from this local.
|
||||
///
|
||||
/// Assumes that `universal_regions` indices map is fully constructed.
|
||||
fn add_implied_bounds(&mut self, indices: &UniversalRegionIndices<'tcx>, ty: Ty<'tcx>) {
|
||||
debug!("add_implied_bounds(ty={:?})", ty);
|
||||
let span = self.infcx.tcx.def_span(self.mir_def_id);
|
||||
let bounds = self.infcx
|
||||
.implied_outlives_bounds(self.param_env, self.mir_node_id, ty, span);
|
||||
self.add_outlives_bounds(indices, bounds);
|
||||
}
|
||||
|
||||
/// Registers the `OutlivesBound` items from `outlives_bounds` in
|
||||
/// the outlives relation as well as the region-bound pairs
|
||||
/// listing.
|
||||
fn add_outlives_bounds<I>(&mut self, indices: &UniversalRegionIndices<'tcx>, outlives_bounds: I)
|
||||
where
|
||||
I: IntoIterator<Item = OutlivesBound<'tcx>>,
|
||||
{
|
||||
for outlives_bound in outlives_bounds {
|
||||
debug!("add_outlives_bounds(bound={:?})", outlives_bound);
|
||||
|
||||
match outlives_bound {
|
||||
OutlivesBound::RegionSubRegion(r1, r2) => {
|
||||
// The bound says that `r1 <= r2`; we store `r2: r1`.
|
||||
let r1 = indices.to_region_vid(r1);
|
||||
let r2 = indices.to_region_vid(r2);
|
||||
self.relations.relate_universal_regions(r2, r1);
|
||||
}
|
||||
|
||||
OutlivesBound::RegionSubParam(r_a, param_b) => {
|
||||
self.region_bound_pairs
|
||||
.push((r_a, GenericKind::Param(param_b)));
|
||||
}
|
||||
|
||||
OutlivesBound::RegionSubProjection(r_a, projection_b) => {
|
||||
self.region_bound_pairs
|
||||
.push((r_a, GenericKind::Projection(projection_b)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UniversalRegionRelations {
|
||||
/// Records in the `outlives_relation` (and
|
||||
/// `inverse_outlives_relation`) that `fr_a: fr_b`.
|
||||
fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
|
||||
debug!(
|
||||
"relate_universal_regions: fr_a={:?} outlives fr_b={:?}",
|
||||
fr_a,
|
||||
fr_b
|
||||
);
|
||||
self.outlives.add(fr_a, fr_b);
|
||||
self.inverse_outlives.add(fr_b, fr_a);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait InferCtxtExt<'tcx> {
|
||||
fn replace_free_regions_with_nll_infer_vars<T>(
|
||||
&self,
|
||||
origin: NLLRegionVariableOrigin,
|
||||
value: &T,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>;
|
||||
|
||||
fn replace_bound_regions_with_nll_infer_vars<T>(
|
||||
&self,
|
||||
origin: NLLRegionVariableOrigin,
|
||||
value: &ty::Binder<T>,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>;
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> {
|
||||
fn replace_free_regions_with_nll_infer_vars<T>(
|
||||
&self,
|
||||
origin: NLLRegionVariableOrigin,
|
||||
value: &T,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
self.tcx.fold_regions(
|
||||
value,
|
||||
&mut false,
|
||||
|_region, _depth| self.next_nll_region_var(origin),
|
||||
)
|
||||
}
|
||||
|
||||
fn replace_bound_regions_with_nll_infer_vars<T>(
|
||||
&self,
|
||||
origin: NLLRegionVariableOrigin,
|
||||
value: &ty::Binder<T>,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
let (value, _map) = self.tcx
|
||||
.replace_late_bound_regions(value, |_br| self.next_nll_region_var(origin));
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UniversalRegionIndices<'tcx> {
|
||||
/// Converts `r` into a local inference variable: `r` can either
|
||||
/// by a `ReVar` (i.e., already a reference to an inference
|
||||
/// variable) or it can be `'static` or some early-bound
|
||||
/// region. This is useful when taking the results from
|
||||
/// type-checking and trait-matching, which may sometimes
|
||||
/// reference those regions from the `ParamEnv`. It is also used
|
||||
/// during initialization. Relies on the `indices` map having been
|
||||
/// fully initialized.
|
||||
pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
|
||||
match r {
|
||||
ty::ReEarlyBound(..) | ty::ReStatic => *self.indices.get(&r).unwrap(),
|
||||
ty::ReVar(..) => r.to_region_vid(),
|
||||
_ => bug!("cannot convert `{:?}` to a region vid", r),
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace all free regions in `value` with region vids, as
|
||||
/// returned by `to_region_vid`.
|
||||
pub fn fold_to_region_vids<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: &T) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
tcx.fold_regions(
|
||||
value,
|
||||
&mut false,
|
||||
|region, _| tcx.mk_region(ty::ReVar(self.to_region_vid(region))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
//! This pass type-checks the MIR to ensure it is not broken.
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
|
||||
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
|
||||
use rustc::infer::region_constraints::RegionConstraintData;
|
||||
use rustc::traits::{self, FulfillmentContext};
|
||||
@ -110,6 +111,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
||||
|
||||
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
|
||||
self.super_constant(constant, location);
|
||||
self.sanitize_constant(constant, location);
|
||||
self.sanitize_type(constant, constant.ty);
|
||||
}
|
||||
|
||||
@ -159,6 +161,52 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the constant's `ty` field matches up with what
|
||||
/// would be expected from its literal.
|
||||
fn sanitize_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
|
||||
debug!(
|
||||
"sanitize_constant(constant={:?}, location={:?})",
|
||||
constant,
|
||||
location
|
||||
);
|
||||
|
||||
let expected_ty = match constant.literal {
|
||||
Literal::Value { value } => value.ty,
|
||||
Literal::Promoted { .. } => {
|
||||
// FIXME -- promoted MIR return types reference
|
||||
// various "free regions" (e.g., scopes and things)
|
||||
// that they ought not to do. We have to figure out
|
||||
// how best to handle that -- probably we want treat
|
||||
// promoted MIR much like closures, renumbering all
|
||||
// their free regions and propagating constraints
|
||||
// upwards. We have the same acyclic guarantees, so
|
||||
// that should be possible. But for now, ignore them.
|
||||
//
|
||||
// let promoted_mir = &self.mir.promoted[index];
|
||||
// promoted_mir.return_ty()
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
debug!("sanitize_constant: expected_ty={:?}", expected_ty);
|
||||
|
||||
if let Err(terr) = self.cx
|
||||
.eq_types(expected_ty, constant.ty, location.at_self())
|
||||
{
|
||||
span_mirbug!(
|
||||
self,
|
||||
constant,
|
||||
"constant {:?} should have type {:?} but has {:?} ({:?})",
|
||||
constant,
|
||||
expected_ty,
|
||||
constant.ty,
|
||||
terr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the types internal to the `place` match up with
|
||||
/// what would be expected.
|
||||
fn sanitize_place(
|
||||
&mut self,
|
||||
place: &Place<'tcx>,
|
||||
@ -1088,14 +1136,45 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
||||
operands: &[Operand<'tcx>],
|
||||
location: Location,
|
||||
) {
|
||||
let tcx = self.tcx();
|
||||
|
||||
match aggregate_kind {
|
||||
// tuple rvalue field type is always the type of the op. Nothing to check here.
|
||||
AggregateKind::Tuple => return,
|
||||
|
||||
// For closures, we have some **extra requirements** we
|
||||
// have to check. In particular, in their upvars and
|
||||
// signatures, closures often reference various regions
|
||||
// from the surrounding function -- we call those the
|
||||
// closure's free regions. When we borrow-check (and hence
|
||||
// region-check) closures, we may find that the closure
|
||||
// requires certain relationships between those free
|
||||
// regions. However, because those free regions refer to
|
||||
// portions of the CFG of their caller, the closure is not
|
||||
// in a position to verify those relationships. In that
|
||||
// case, the requirements get "propagated" to us, and so
|
||||
// we have to solve them here where we instantiate the
|
||||
// closure.
|
||||
//
|
||||
// Despite the opacity of the previous parapgrah, this is
|
||||
// actually relatively easy to understand in terms of the
|
||||
// desugaring. A closure gets desugared to a struct, and
|
||||
// these extra requirements are basically like where
|
||||
// clauses on the struct.
|
||||
AggregateKind::Closure(def_id, substs) => {
|
||||
if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) {
|
||||
closure_region_requirements.apply_requirements(
|
||||
self.infcx,
|
||||
location,
|
||||
*def_id,
|
||||
*substs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let tcx = self.tcx();
|
||||
|
||||
for (i, operand) in operands.iter().enumerate() {
|
||||
let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) {
|
||||
Ok(field_ty) => field_ty,
|
||||
|
@ -11,7 +11,8 @@
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc::mir::*;
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::mir::visit::Visitor;
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
use rustc::ty::item_path;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
@ -56,9 +57,19 @@ pub enum PassWhere {
|
||||
/// where `<filter>` takes the following forms:
|
||||
///
|
||||
/// - `all` -- dump MIR for all fns, all passes, all everything
|
||||
/// - `substring1&substring2,...` -- `&`-separated list of substrings
|
||||
/// that can appear in the pass-name or the `item_path_str` for the given
|
||||
/// node-id. If any one of the substrings match, the data is dumped out.
|
||||
/// - a filter defined by a set of substrings combined with `&` and `|`
|
||||
/// (`&` has higher precedence). At least one of the `|`-separated groups
|
||||
/// must match; an `|`-separated group matches if all of its `&`-separated
|
||||
/// substrings are matched.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// - `nll` == match if `nll` appears in the name
|
||||
/// - `foo & nll` == match if `foo` and `nll` both appear in the name
|
||||
/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
|
||||
/// or `typeck` appears in the name.
|
||||
/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
|
||||
/// or `typeck` and `bar` both appear in the name.
|
||||
pub fn dump_mir<'a, 'gcx, 'tcx, F>(
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
pass_num: Option<&Display>,
|
||||
@ -103,8 +114,10 @@ pub fn dump_enabled<'a, 'gcx, 'tcx>(
|
||||
// see notes on #41697 below
|
||||
tcx.item_path_str(source.def_id)
|
||||
});
|
||||
filters.split("&").any(|filter| {
|
||||
filter == "all" || pass_name.contains(filter) || node_path.contains(filter)
|
||||
filters.split("|").any(|or_filter| {
|
||||
or_filter.split("&").all(|and_filter| {
|
||||
and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -125,14 +138,7 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(
|
||||
F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
|
||||
{
|
||||
let _: io::Result<()> = do catch {
|
||||
let mut file = create_dump_file(
|
||||
tcx,
|
||||
"mir",
|
||||
pass_num,
|
||||
pass_name,
|
||||
disambiguator,
|
||||
source,
|
||||
)?;
|
||||
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?;
|
||||
writeln!(file, "// MIR for `{}`", node_path)?;
|
||||
writeln!(file, "// source = {:?}", source)?;
|
||||
writeln!(file, "// pass_name = {}", pass_name)?;
|
||||
@ -148,15 +154,9 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(
|
||||
};
|
||||
|
||||
if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
|
||||
let _: io::Result<()> = do catch {
|
||||
let mut file = create_dump_file(
|
||||
tcx,
|
||||
"dot",
|
||||
pass_num,
|
||||
pass_name,
|
||||
disambiguator,
|
||||
source,
|
||||
)?;
|
||||
let _: io::Result<()> = do catch {
|
||||
let mut file =
|
||||
create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?;
|
||||
write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?;
|
||||
Ok(())
|
||||
};
|
||||
@ -297,10 +297,10 @@ where
|
||||
}
|
||||
|
||||
/// Write out a human-readable textual representation for the given basic block.
|
||||
pub fn write_basic_block<F>(
|
||||
tcx: TyCtxt,
|
||||
pub fn write_basic_block<'cx, 'gcx, 'tcx, F>(
|
||||
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
|
||||
block: BasicBlock,
|
||||
mir: &Mir,
|
||||
mir: &Mir<'tcx>,
|
||||
extra_data: &mut F,
|
||||
w: &mut Write,
|
||||
) -> io::Result<()>
|
||||
@ -330,6 +330,11 @@ where
|
||||
comment(tcx, statement.source_info),
|
||||
A = ALIGN,
|
||||
)?;
|
||||
|
||||
write_extra(tcx, w, |visitor| {
|
||||
visitor.visit_statement(current_location.block, statement, current_location);
|
||||
})?;
|
||||
|
||||
extra_data(PassWhere::AfterLocation(current_location), w)?;
|
||||
|
||||
current_location.statement_index += 1;
|
||||
@ -346,11 +351,94 @@ where
|
||||
comment(tcx, data.terminator().source_info),
|
||||
A = ALIGN,
|
||||
)?;
|
||||
|
||||
write_extra(tcx, w, |visitor| {
|
||||
visitor.visit_terminator(current_location.block, data.terminator(), current_location);
|
||||
})?;
|
||||
|
||||
extra_data(PassWhere::AfterLocation(current_location), w)?;
|
||||
|
||||
writeln!(w, "{}}}", INDENT)
|
||||
}
|
||||
|
||||
/// After we print the main statement, we sometimes dump extra
|
||||
/// information. There's often a lot of little things "nuzzled up" in
|
||||
/// a statement.
|
||||
fn write_extra<'cx, 'gcx, 'tcx, F>(
|
||||
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
|
||||
write: &mut Write,
|
||||
mut visit_op: F,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(&mut ExtraComments<'cx, 'gcx, 'tcx>),
|
||||
{
|
||||
let mut extra_comments = ExtraComments {
|
||||
_tcx: tcx,
|
||||
comments: vec![],
|
||||
};
|
||||
visit_op(&mut extra_comments);
|
||||
for comment in extra_comments.comments {
|
||||
writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct ExtraComments<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
|
||||
_tcx: TyCtxt<'cx, 'gcx, 'tcx>, // don't need it now, but bet we will soon
|
||||
comments: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> ExtraComments<'cx, 'gcx, 'tcx> {
|
||||
fn push(&mut self, lines: &str) {
|
||||
for line in lines.split("\n") {
|
||||
self.comments.push(line.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ExtraComments<'cx, 'gcx, 'tcx> {
|
||||
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
|
||||
self.super_constant(constant, location);
|
||||
let Constant { span, ty, literal } = constant;
|
||||
self.push(&format!("mir::Constant"));
|
||||
self.push(&format!("└ span: {:?}", span));
|
||||
self.push(&format!("└ ty: {:?}", ty));
|
||||
self.push(&format!("└ literal: {:?}", literal));
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
|
||||
self.super_const(constant);
|
||||
let ty::Const { ty, val } = constant;
|
||||
self.push(&format!("ty::Const"));
|
||||
self.push(&format!("└ ty: {:?}", ty));
|
||||
self.push(&format!("└ val: {:?}", val));
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
self.super_rvalue(rvalue, location);
|
||||
match rvalue {
|
||||
Rvalue::Aggregate(kind, _) => match **kind {
|
||||
AggregateKind::Closure(def_id, substs) => {
|
||||
self.push(&format!("closure"));
|
||||
self.push(&format!("└ def_id: {:?}", def_id));
|
||||
self.push(&format!("└ substs: {:#?}", substs));
|
||||
}
|
||||
|
||||
AggregateKind::Generator(def_id, substs, interior) => {
|
||||
self.push(&format!("generator"));
|
||||
self.push(&format!("└ def_id: {:?}", def_id));
|
||||
self.push(&format!("└ substs: {:#?}", substs));
|
||||
self.push(&format!("└ interior: {:?}", interior));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
|
||||
format!(
|
||||
"scope {} at {}",
|
||||
|
@ -29,7 +29,7 @@ use value::Value;
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::layout::{HasDataLayout, LayoutOf};
|
||||
use rustc::ty::subst::{Kind, Subst, Substs};
|
||||
use rustc::ty::subst::{Kind, Substs};
|
||||
use rustc::hir;
|
||||
|
||||
use libc::{c_uint, c_char};
|
||||
@ -393,7 +393,7 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
ty::TyFnPtr(_) => ty.fn_sig(ccx.tcx()),
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
let tcx = ccx.tcx();
|
||||
let sig = tcx.fn_sig(def_id).subst(tcx, substs.substs);
|
||||
let sig = substs.closure_sig(def_id, tcx);
|
||||
|
||||
let env_ty = tcx.closure_env_ty(def_id, substs).unwrap();
|
||||
sig.map_bound(|sig| tcx.mk_fn_sig(
|
||||
|
@ -20,7 +20,7 @@ use rustc::mir::tcx::PlaceTy;
|
||||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc::ty::layout::{self, LayoutOf, Size};
|
||||
use rustc::ty::cast::{CastTy, IntTy};
|
||||
use rustc::ty::subst::{Kind, Substs, Subst};
|
||||
use rustc::ty::subst::{Kind, Substs};
|
||||
use rustc_apfloat::{ieee, Float, Status};
|
||||
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||
use base;
|
||||
@ -658,8 +658,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
||||
.find(|it| it.kind == ty::AssociatedKind::Method)
|
||||
.unwrap().def_id;
|
||||
// Now create its substs [Closure, Tuple]
|
||||
let input = tcx.fn_sig(def_id)
|
||||
.subst(tcx, substs.substs).input(0);
|
||||
let input = substs.closure_sig(def_id, tcx).input(0);
|
||||
let input = tcx.erase_late_bound_regions_and_normalize(&input);
|
||||
let substs = tcx.mk_substs([operand.ty, input]
|
||||
.iter().cloned().map(Kind::from));
|
||||
|
@ -12,7 +12,7 @@ use rustc::hir::def_id::DefId;
|
||||
use rustc::middle::lang_items::DropInPlaceFnLangItem;
|
||||
use rustc::traits;
|
||||
use rustc::ty::adjustment::CustomCoerceUnsized;
|
||||
use rustc::ty::subst::{Kind, Subst};
|
||||
use rustc::ty::subst::Kind;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
|
||||
pub use rustc::ty::Instance;
|
||||
@ -34,7 +34,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([
|
||||
|
@ -16,7 +16,6 @@ use hir::def::Def;
|
||||
use hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc::{infer, traits};
|
||||
use rustc::ty::{self, TyCtxt, TypeFoldable, LvaluePreference, Ty};
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
|
||||
use syntax::abi;
|
||||
use syntax::symbol::Symbol;
|
||||
@ -109,7 +108,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// haven't yet decided on whether the closure is fn vs
|
||||
// fnmut vs fnonce. If so, we have to defer further processing.
|
||||
if self.closure_kind(def_id, substs).is_none() {
|
||||
let closure_ty = self.fn_sig(def_id).subst(self.tcx, substs.substs);
|
||||
let closure_ty = self.closure_sig(def_id, substs);
|
||||
let fn_sig = self.replace_late_bound_regions_with_fresh_var(call_expr.span,
|
||||
infer::FnCall,
|
||||
&closure_ty)
|
||||
|
@ -74,7 +74,6 @@ use rustc::ty::{self, LvaluePreference, TypeAndMut,
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::error::TypeError;
|
||||
use rustc::ty::relate::RelateResult;
|
||||
use rustc::ty::subst::Subst;
|
||||
use errors::DiagnosticBuilder;
|
||||
use syntax::abi;
|
||||
use syntax::feature_gate;
|
||||
@ -670,7 +669,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
|
||||
// to
|
||||
// `fn(arg0,arg1,...) -> _`
|
||||
let sig = self.fn_sig(def_id_a).subst(self.tcx, substs_a.substs);
|
||||
let sig = self.closure_sig(def_id_a, substs_a);
|
||||
let converted_sig = sig.map_bound(|s| {
|
||||
let params_iter = match s.inputs()[0].sty {
|
||||
ty::TyTuple(params, _) => {
|
||||
|
@ -1268,15 +1268,23 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
))
|
||||
}
|
||||
|
||||
NodeExpr(&hir::Expr { node: hir::ExprClosure(..), hir_id, .. }) => {
|
||||
let tables = tcx.typeck_tables_of(def_id);
|
||||
match tables.node_id_to_type(hir_id).sty {
|
||||
ty::TyClosure(closure_def_id, closure_substs) => {
|
||||
assert_eq!(def_id, closure_def_id);
|
||||
return closure_substs.closure_sig(closure_def_id, tcx);
|
||||
}
|
||||
ref t => bug!("closure with non-closure type: {:?}", t),
|
||||
}
|
||||
NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => {
|
||||
// Closure signatures are not like other function
|
||||
// signatures and cannot be accessed through `fn_sig`. For
|
||||
// example, a closure signature excludes the `self`
|
||||
// argument. In any case they are embedded within the
|
||||
// closure type as part of the `ClosureSubsts`.
|
||||
//
|
||||
// To get
|
||||
// the signature of a closure, you should use the
|
||||
// `closure_sig` method on the `ClosureSubsts`:
|
||||
//
|
||||
// closure_substs.closure_sig(def_id, tcx)
|
||||
//
|
||||
// or, inside of an inference context, you can use
|
||||
//
|
||||
// infcx.closure_sig(def_id, closure_substs)
|
||||
bug!("to get the signature of a closure, use `closure_sig()` not `fn_sig()`");
|
||||
}
|
||||
|
||||
x => {
|
||||
|
@ -717,6 +717,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
||||
is just used for rustc unit tests \
|
||||
and will never be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
("rustc_regions", Normal, Gated(Stability::Unstable,
|
||||
"rustc_attrs",
|
||||
"the `#[rustc_regions]` attribute \
|
||||
is just used for rustc unit tests \
|
||||
and will never be stable",
|
||||
cfg_fn!(rustc_attrs))),
|
||||
("rustc_error", Whitelisted, Gated(Stability::Unstable,
|
||||
"rustc_attrs",
|
||||
"the `#[rustc_error]` attribute \
|
||||
|
@ -123,7 +123,12 @@ impl<'a> From<&'a str> for Symbol {
|
||||
|
||||
impl fmt::Debug for Symbol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}({})", self, self.0)
|
||||
let is_gensymed = with_interner(|interner| interner.is_gensymed(*self));
|
||||
if is_gensymed {
|
||||
write!(f, "{}({})", self, self.0)
|
||||
} else {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,6 +206,10 @@ impl Interner {
|
||||
Symbol(!0 - self.gensyms.len() as u32 + 1)
|
||||
}
|
||||
|
||||
fn is_gensymed(&mut self, symbol: Symbol) -> bool {
|
||||
symbol.0 as usize >= self.strings.len()
|
||||
}
|
||||
|
||||
pub fn get(&self, symbol: Symbol) -> &str {
|
||||
match self.strings.get(symbol.0 as usize) {
|
||||
Some(ref string) => string,
|
||||
|
@ -8,17 +8,27 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: ll nll
|
||||
//[nll] compile-flags: -Znll -Zborrowck=mir
|
||||
|
||||
fn static_id<'a,'b>(t: &'a ()) -> &'static ()
|
||||
where 'a: 'static { t }
|
||||
fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
|
||||
where 'a: 'b, 'b: 'static { t }
|
||||
fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
|
||||
t //~ ERROR E0312
|
||||
t //[ll]~ ERROR E0312
|
||||
//[nll]~^ WARNING not reporting region error due to -Znll
|
||||
//[nll]~| ERROR free region `'a` does not outlive free region `'static`
|
||||
}
|
||||
|
||||
fn error(u: &(), v: &()) {
|
||||
static_id(&u); //~ ERROR cannot infer an appropriate lifetime
|
||||
static_id_indirect(&v); //~ ERROR cannot infer an appropriate lifetime
|
||||
static_id(&u); //[ll]~ ERROR cannot infer an appropriate lifetime
|
||||
//[nll]~^ WARNING not reporting region error due to -Znll
|
||||
static_id_indirect(&v); //[ll]~ ERROR cannot infer an appropriate lifetime
|
||||
//[nll]~^ WARNING not reporting region error due to -Znll
|
||||
|
||||
// FIXME(#45827) -- MIR type checker shortcomings mean we don't
|
||||
// see these errors (yet) in nll mode.
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -28,18 +28,18 @@ fn main() {
|
||||
// START rustc.main.nll.0.mir
|
||||
// | Live variables on entry to bb0: []
|
||||
// bb0: {
|
||||
// | Live variables at bb0[0]: []
|
||||
// | Live variables on entry to bb0[0]: []
|
||||
// StorageLive(_1);
|
||||
// | Live variables at bb0[1]: []
|
||||
// | Live variables on entry to bb0[1]: []
|
||||
// _1 = const <std::boxed::Box<T>>::new(const 22usize) -> [return: bb2, unwind: bb1];
|
||||
// }
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// | Live variables on entry to bb2: [_1 (drop)]
|
||||
// bb2: {
|
||||
// | Live variables at bb2[0]: [_1 (drop)]
|
||||
// | Live variables on entry to bb2[0]: [_1 (drop)]
|
||||
// StorageLive(_2);
|
||||
// | Live variables at bb2[1]: [_1 (drop)]
|
||||
// | Live variables on entry to bb2[1]: [_1 (drop)]
|
||||
// _2 = const can_panic() -> [return: bb3, unwind: bb4];
|
||||
// }
|
||||
// END rustc.main.nll.0.mir
|
||||
|
@ -27,15 +27,15 @@ fn main() {
|
||||
// START rustc.main.nll.0.mir
|
||||
// | Live variables on entry to bb2: []
|
||||
// bb2: {
|
||||
// | Live variables at bb2[0]: []
|
||||
// | Live variables on entry to bb2[0]: []
|
||||
// _1 = const 55usize;
|
||||
// | Live variables at bb2[1]: [_1]
|
||||
// | Live variables on entry to bb2[1]: [_1]
|
||||
// StorageLive(_3);
|
||||
// | Live variables at bb2[2]: [_1]
|
||||
// | Live variables on entry to bb2[2]: [_1]
|
||||
// StorageLive(_4);
|
||||
// | Live variables at bb2[3]: [_1]
|
||||
// | Live variables on entry to bb2[3]: [_1]
|
||||
// _4 = _1;
|
||||
// | Live variables at bb2[4]: [_4]
|
||||
// | Live variables on entry to bb2[4]: [_4]
|
||||
// _3 = const use_x(move _4) -> [return: bb3, unwind: bb1];
|
||||
// }
|
||||
// END rustc.main.nll.0.mir
|
||||
|
@ -31,18 +31,18 @@ fn main() {
|
||||
// START rustc.main.nll.0.mir
|
||||
// | Live variables on entry to bb3: [_1]
|
||||
// bb3: {
|
||||
// | Live variables at bb3[0]: [_1]
|
||||
// | Live variables on entry to bb3[0]: [_1]
|
||||
// StorageLive(_4);
|
||||
// | Live variables at bb3[1]: [_1]
|
||||
// | Live variables on entry to bb3[1]: [_1]
|
||||
// _4 = _1;
|
||||
// | Live variables at bb3[2]: [_4]
|
||||
// | Live variables on entry to bb3[2]: [_4]
|
||||
// _3 = const make_live(move _4) -> [return: bb5, unwind: bb1];
|
||||
// }
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// | Live variables on entry to bb4: []
|
||||
// bb4: {
|
||||
// | Live variables at bb4[0]: []
|
||||
// | Live variables on entry to bb4[0]: []
|
||||
// _5 = const make_dead() -> [return: bb6, unwind: bb1];
|
||||
// }
|
||||
// END rustc.main.nll.0.mir
|
||||
|
@ -26,9 +26,18 @@ fn main() {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.use_x.nll.0.mir
|
||||
// | '_#0r: {bb0[0], bb0[1], '_#0r}
|
||||
// | '_#1r: {bb0[0], bb0[1], '_#1r}
|
||||
// | '_#2r: {bb0[0], bb0[1], '_#2r}
|
||||
// | '_#3r: {bb0[0], bb0[1], '_#3r}
|
||||
// | Free Region Mapping
|
||||
// | '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#3r]
|
||||
// | '_#1r | External | ['_#1r]
|
||||
// | '_#2r | External | ['_#2r, '_#1r]
|
||||
// | '_#3r | Local | ['_#3r]
|
||||
// |
|
||||
// | Inferred Region Values
|
||||
// | '_#0r | {bb0[0], bb0[1], '_#0r}
|
||||
// | '_#1r | {bb0[0], bb0[1], '_#1r}
|
||||
// | '_#2r | {bb0[0], bb0[1], '_#2r}
|
||||
// | '_#3r | {bb0[0], bb0[1], '_#3r}
|
||||
// |
|
||||
// ...
|
||||
// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool {
|
||||
// END rustc.use_x.nll.0.mir
|
||||
|
@ -28,11 +28,10 @@ fn main() {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#6r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]}
|
||||
// | '_#6r | {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]}
|
||||
// ...
|
||||
// | '_#8r | {bb0[11], bb0[12], bb0[13], bb0[14]}
|
||||
// ...
|
||||
// | '_#8r: {bb0[11], bb0[12], bb0[13], bb0[14]}
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// let _2: &'_#6r mut i32;
|
||||
// ...
|
||||
// let _4: &'_#8r mut i32;
|
||||
|
@ -31,26 +31,26 @@ fn main() {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]}
|
||||
// | '_#2r: {bb2[1], bb3[0], bb3[1]}
|
||||
// | '_#1r | {bb2[0], bb2[1], bb3[0], bb3[1]}
|
||||
// | '_#2r | {bb2[1], bb3[0], bb3[1]}
|
||||
// ...
|
||||
// let _2: &'_#2r usize;
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// bb2: {
|
||||
// | Live variables at bb2[0]: [_1, _3]
|
||||
// | Live variables on entry to bb2[0]: [_1, _3]
|
||||
// _2 = &'_#1r _1[_3];
|
||||
// | Live variables at bb2[1]: [_2]
|
||||
// | Live variables on entry to bb2[1]: [_2]
|
||||
// switchInt(const true) -> [0u8: bb4, otherwise: bb3];
|
||||
// }
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// bb3: {
|
||||
// | Live variables at bb3[0]: [_2]
|
||||
// | Live variables on entry to bb3[0]: [_2]
|
||||
// StorageLive(_7);
|
||||
// | Live variables at bb3[1]: [_2]
|
||||
// | Live variables on entry to bb3[1]: [_2]
|
||||
// _7 = (*_2);
|
||||
// | Live variables at bb3[2]: [_7]
|
||||
// | Live variables on entry to bb3[2]: [_7]
|
||||
// _6 = const use_x(move _7) -> [return: bb5, unwind: bb1];
|
||||
// }
|
||||
// END rustc.main.nll.0.mir
|
||||
|
@ -44,5 +44,5 @@ unsafe impl<#[may_dangle] T> Drop for Wrap<T> {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#5r: {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]}
|
||||
// | '_#5r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1]}
|
||||
// END rustc.main.nll.0.mir
|
||||
|
@ -46,5 +46,5 @@ impl<T> Drop for Wrap<T> {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#5r: {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]}
|
||||
// | '_#5r | {bb2[3], bb2[4], bb2[5], bb3[0], bb3[1], bb3[2], bb4[0], bb5[0], bb5[1], bb5[2], bb6[0], bb7[0], bb7[1], bb8[0]}
|
||||
// END rustc.main.nll.0.mir
|
||||
|
@ -36,10 +36,10 @@ fn main() {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]}
|
||||
// | '_#1r | {bb2[0], bb2[1], bb3[0], bb3[1]}
|
||||
// ...
|
||||
// | '_#3r: {bb8[1], bb8[2], bb8[3], bb8[4]}
|
||||
// | '_#4r: {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]}
|
||||
// | '_#3r | {bb8[1], bb8[2], bb8[3], bb8[4]}
|
||||
// | '_#4r | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]}
|
||||
// ...
|
||||
// let mut _2: &'_#4r usize;
|
||||
// ...
|
||||
|
@ -32,9 +32,9 @@ fn main() {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#1r: {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
|
||||
// | '_#2r: {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
|
||||
// | '_#3r: {bb2[5], bb2[6], bb3[0], bb3[1]}
|
||||
// | '_#1r | {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
|
||||
// | '_#2r | {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
|
||||
// | '_#3r | {bb2[5], bb2[6], bb3[0], bb3[1]}
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// let _2: &'_#2r usize;
|
||||
|
50
src/test/ui/nll/capture-ref-in-struct.rs
Normal file
50
src/test/ui/nll/capture-ref-in-struct.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir
|
||||
|
||||
// Test that a structure which tries to store a pointer to `y` into
|
||||
// `p` (indirectly) fails to compile.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
struct SomeStruct<'a, 'b: 'a> {
|
||||
p: &'a mut &'b i32,
|
||||
y: &'b i32,
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let x = 44;
|
||||
let mut p = &x;
|
||||
|
||||
{
|
||||
let y = 22;
|
||||
|
||||
let closure = SomeStruct {
|
||||
p: &mut p,
|
||||
y: &y,
|
||||
};
|
||||
|
||||
closure.invoke();
|
||||
}
|
||||
//~^ ERROR borrowed value does not live long enough [E0597]
|
||||
|
||||
deref(p);
|
||||
}
|
||||
|
||||
impl<'a, 'b> SomeStruct<'a, 'b> {
|
||||
fn invoke(self) {
|
||||
*self.p = self.y;
|
||||
}
|
||||
}
|
||||
|
||||
fn deref(_: &i32) { }
|
||||
|
||||
fn main() { }
|
13
src/test/ui/nll/capture-ref-in-struct.stderr
Normal file
13
src/test/ui/nll/capture-ref-in-struct.stderr
Normal file
@ -0,0 +1,13 @@
|
||||
error[E0597]: borrowed value does not live long enough
|
||||
--> $DIR/capture-ref-in-struct.rs:36:6
|
||||
|
|
||||
28 | let y = 22;
|
||||
| - temporary value created here
|
||||
...
|
||||
36 | }
|
||||
| ^ temporary value dropped here while still borrowed
|
||||
|
|
||||
= note: consider using a `let` binding to increase its lifetime
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,53 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test closure that:
|
||||
//
|
||||
// - takes an argument `y` with lifetime `'a` (in the code, it's anonymous)
|
||||
// - stores `y` into another, longer-lived spot with lifetime `'b`
|
||||
//
|
||||
// Because `'a` and `'b` are two different, unrelated higher-ranked
|
||||
// regions with no relationship to one another, this is an error. This
|
||||
// error is reported by the closure itself and is not propagated to
|
||||
// its creator: this is because `'a` and `'b` are higher-ranked
|
||||
// (late-bound) regions and the closure is not allowed to propagate
|
||||
// additional where clauses between higher-ranked regions, only those
|
||||
// that appear free in its type (hence, we see it before the closure's
|
||||
// "external requirements" report).
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_regions]
|
||||
fn test() {
|
||||
let x = 44;
|
||||
let mut p = &x;
|
||||
|
||||
{
|
||||
let y = 22;
|
||||
let mut closure = expect_sig(|p, y| *p = y);
|
||||
//~^ ERROR free region `'_#4r` does not outlive free region `'_#3r`
|
||||
//~| WARNING not reporting region error due to -Znll
|
||||
closure(&mut p, &y);
|
||||
}
|
||||
|
||||
deref(p);
|
||||
}
|
||||
|
||||
fn expect_sig<F>(f: F) -> F
|
||||
where F: FnMut(&mut &i32, &i32)
|
||||
{
|
||||
f
|
||||
}
|
||||
|
||||
fn deref(_p: &i32) { }
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,40 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/escape-argument-callee.rs:36:50
|
||||
|
|
||||
36 | let mut closure = expect_sig(|p, y| *p = y);
|
||||
| ^
|
||||
|
||||
error: free region `'_#4r` does not outlive free region `'_#3r`
|
||||
--> $DIR/escape-argument-callee.rs:36:45
|
||||
|
|
||||
36 | let mut closure = expect_sig(|p, y| *p = y);
|
||||
| ^^^^^^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/escape-argument-callee.rs:36:38
|
||||
|
|
||||
36 | let mut closure = expect_sig(|p, y| *p = y);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: defining type: DefId(0/1:9 ~ escape_argument_callee[317d]::test[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) i32))
|
||||
]
|
||||
= note: number of external vids: 1
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/escape-argument-callee.rs:30:1
|
||||
|
|
||||
30 | / fn test() {
|
||||
31 | | let x = 44;
|
||||
32 | | let mut p = &x;
|
||||
33 | |
|
||||
... |
|
||||
42 | | deref(p);
|
||||
43 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:3 ~ escape_argument_callee[317d]::test[0]) with substs []
|
||||
|
||||
error: aborting due to previous error
|
||||
|
52
src/test/ui/nll/closure-requirements/escape-argument.rs
Normal file
52
src/test/ui/nll/closure-requirements/escape-argument.rs
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test closure that:
|
||||
//
|
||||
// - takes an argument `y`
|
||||
// - stores `y` into another, longer-lived spot
|
||||
//
|
||||
// but is invoked with a spot that doesn't live long
|
||||
// enough to store `y`.
|
||||
//
|
||||
// The error is reported in the caller: invoking the closure links the
|
||||
// lifetime of the variable that is given as `y` (via subtyping) and
|
||||
// thus forces the corresponding borrow to live too long. This is
|
||||
// basically checking that the MIR type checker correctly enforces the
|
||||
// closure signature.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_regions]
|
||||
fn test() {
|
||||
let x = 44;
|
||||
let mut p = &x;
|
||||
|
||||
{
|
||||
let y = 22;
|
||||
let mut closure = expect_sig(|p, y| *p = y);
|
||||
closure(&mut p, &y);
|
||||
}
|
||||
//~^ ERROR borrowed value does not live long enough [E0597]
|
||||
|
||||
deref(p);
|
||||
}
|
||||
|
||||
fn expect_sig<F>(f: F) -> F
|
||||
where F: for<'a, 'b> FnMut(&'a mut &'b i32, &'b i32)
|
||||
{
|
||||
f
|
||||
}
|
||||
|
||||
fn deref(_p: &i32) { }
|
||||
|
||||
fn main() { }
|
39
src/test/ui/nll/closure-requirements/escape-argument.stderr
Normal file
39
src/test/ui/nll/closure-requirements/escape-argument.stderr
Normal file
@ -0,0 +1,39 @@
|
||||
note: External requirements
|
||||
--> $DIR/escape-argument.rs:36:38
|
||||
|
|
||||
36 | let mut closure = expect_sig(|p, y| *p = y);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: defining type: DefId(0/1:9 ~ escape_argument[317d]::test[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32))
|
||||
]
|
||||
= note: number of external vids: 1
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/escape-argument.rs:30:1
|
||||
|
|
||||
30 | / fn test() {
|
||||
31 | | let x = 44;
|
||||
32 | | let mut p = &x;
|
||||
33 | |
|
||||
... |
|
||||
41 | | deref(p);
|
||||
42 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:3 ~ escape_argument[317d]::test[0]) with substs []
|
||||
|
||||
error[E0597]: borrowed value does not live long enough
|
||||
--> $DIR/escape-argument.rs:38:6
|
||||
|
|
||||
35 | let y = 22;
|
||||
| - temporary value created here
|
||||
...
|
||||
38 | }
|
||||
| ^ temporary value dropped here while still borrowed
|
||||
|
|
||||
= note: consider using a `let` binding to increase its lifetime
|
||||
|
||||
error: aborting due to previous error
|
||||
|
43
src/test/ui/nll/closure-requirements/escape-upvar-nested.rs
Normal file
43
src/test/ui/nll/closure-requirements/escape-upvar-nested.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// As in `escape-upvar-ref.rs`, test closure that:
|
||||
//
|
||||
// - captures a variable `y`
|
||||
// - stores reference to `y` into another, longer-lived spot
|
||||
//
|
||||
// except that the closure does so via a second closure.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_regions]
|
||||
fn test() {
|
||||
let x = 44;
|
||||
let mut p = &x;
|
||||
|
||||
{
|
||||
let y = 22;
|
||||
|
||||
let mut closure = || {
|
||||
let mut closure1 = || p = &y;
|
||||
closure1();
|
||||
};
|
||||
|
||||
closure();
|
||||
} //~ ERROR borrowed value does not live long enough
|
||||
|
||||
deref(p);
|
||||
}
|
||||
|
||||
fn deref(_p: &i32) { }
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,61 @@
|
||||
note: External requirements
|
||||
--> $DIR/escape-upvar-nested.rs:31:32
|
||||
|
|
||||
31 | let mut closure1 = || p = &y;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: defining type: DefId(0/1:10 ~ escape_upvar_nested[317d]::test[0]::{{closure}}[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
extern "rust-call" fn(()),
|
||||
&'_#1r mut &'_#2r i32,
|
||||
&'_#3r i32
|
||||
]
|
||||
= note: number of external vids: 4
|
||||
= note: where '_#3r: '_#2r
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/escape-upvar-nested.rs:30:27
|
||||
|
|
||||
30 | let mut closure = || {
|
||||
| ___________________________^
|
||||
31 | | let mut closure1 = || p = &y;
|
||||
32 | | closure1();
|
||||
33 | | };
|
||||
| |_________^
|
||||
|
|
||||
= note: defining type: DefId(0/1:9 ~ escape_upvar_nested[317d]::test[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
extern "rust-call" fn(()),
|
||||
&'_#1r mut &'_#2r i32,
|
||||
&'_#3r i32
|
||||
]
|
||||
= note: number of external vids: 4
|
||||
= note: where '_#3r: '_#2r
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/escape-upvar-nested.rs:23:1
|
||||
|
|
||||
23 | / fn test() {
|
||||
24 | | let x = 44;
|
||||
25 | | let mut p = &x;
|
||||
26 | |
|
||||
... |
|
||||
38 | | deref(p);
|
||||
39 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:3 ~ escape_upvar_nested[317d]::test[0]) with substs []
|
||||
|
||||
error[E0597]: borrowed value does not live long enough
|
||||
--> $DIR/escape-upvar-nested.rs:36:6
|
||||
|
|
||||
28 | let y = 22;
|
||||
| - temporary value created here
|
||||
...
|
||||
36 | } //~ ERROR borrowed value does not live long enough
|
||||
| ^ temporary value dropped here while still borrowed
|
||||
|
|
||||
= note: consider using a `let` binding to increase its lifetime
|
||||
|
||||
error: aborting due to previous error
|
||||
|
42
src/test/ui/nll/closure-requirements/escape-upvar-ref.rs
Normal file
42
src/test/ui/nll/closure-requirements/escape-upvar-ref.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test closure that:
|
||||
//
|
||||
// - captures a variable `y` by reference
|
||||
// - stores that reference to `y` into another, longer-lived place (`p`)
|
||||
//
|
||||
// Both of these are upvars of reference type (the capture of `y` is
|
||||
// of type `&'a i32`, the capture of `p` is of type `&mut &'b
|
||||
// i32`). The closure thus computes a relationship between `'a` and
|
||||
// `'b`. This relationship is propagated to the closure creator,
|
||||
// which reports an error.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_regions]
|
||||
fn test() {
|
||||
let x = 44;
|
||||
let mut p = &x;
|
||||
|
||||
{
|
||||
let y = 22;
|
||||
let mut closure = || p = &y;
|
||||
closure();
|
||||
} //~ ERROR borrowed value does not live long enough
|
||||
|
||||
deref(p);
|
||||
}
|
||||
|
||||
fn deref(_p: &i32) { }
|
||||
|
||||
fn main() { }
|
42
src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
Normal file
42
src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
Normal file
@ -0,0 +1,42 @@
|
||||
note: External requirements
|
||||
--> $DIR/escape-upvar-ref.rs:33:27
|
||||
|
|
||||
33 | let mut closure = || p = &y;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: defining type: DefId(0/1:9 ~ escape_upvar_ref[317d]::test[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
extern "rust-call" fn(()),
|
||||
&'_#1r mut &'_#2r i32,
|
||||
&'_#3r i32
|
||||
]
|
||||
= note: number of external vids: 4
|
||||
= note: where '_#3r: '_#2r
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/escape-upvar-ref.rs:27:1
|
||||
|
|
||||
27 | / fn test() {
|
||||
28 | | let x = 44;
|
||||
29 | | let mut p = &x;
|
||||
30 | |
|
||||
... |
|
||||
37 | | deref(p);
|
||||
38 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:3 ~ escape_upvar_ref[317d]::test[0]) with substs []
|
||||
|
||||
error[E0597]: borrowed value does not live long enough
|
||||
--> $DIR/escape-upvar-ref.rs:35:6
|
||||
|
|
||||
32 | let y = 22;
|
||||
| - temporary value created here
|
||||
...
|
||||
35 | } //~ ERROR borrowed value does not live long enough
|
||||
| ^ temporary value dropped here while still borrowed
|
||||
|
|
||||
= note: consider using a `let` binding to increase its lifetime
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,63 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test where we fail to approximate due to demanding a postdom
|
||||
// relationship between our upper bounds.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// Callee knows that:
|
||||
//
|
||||
// 'x: 'a
|
||||
// 'x: 'b
|
||||
// 'c: 'y
|
||||
//
|
||||
// we have to prove that `'x: 'y`. We currently can only approximate
|
||||
// via a postdominator -- hence we fail to choose between `'a` and
|
||||
// `'b` here and report the error in the closure.
|
||||
fn establish_relationships<'a, 'b, 'c, F>(
|
||||
_cell_a: Cell<&'a u32>,
|
||||
_cell_b: Cell<&'b u32>,
|
||||
_cell_c: Cell<&'c u32>,
|
||||
_closure: F,
|
||||
) where
|
||||
F: for<'x, 'y> FnMut(
|
||||
Cell<&'a &'x u32>, // shows that 'x: 'a
|
||||
Cell<&'b &'x u32>, // shows that 'x: 'b
|
||||
Cell<&'y &'c u32>, // shows that 'c: 'y
|
||||
Cell<&'x u32>,
|
||||
Cell<&'y u32>,
|
||||
),
|
||||
{
|
||||
}
|
||||
|
||||
fn demand_y<'x, 'y>(_cell_x: Cell<&'x u32>, _cell_y: Cell<&'y u32>, _y: &'y u32) {}
|
||||
|
||||
#[rustc_regions]
|
||||
fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) {
|
||||
establish_relationships(
|
||||
cell_a,
|
||||
cell_b,
|
||||
cell_c,
|
||||
|_outlives1, _outlives2, _outlives3, x, y| {
|
||||
// Only works if 'x: 'y:
|
||||
let p = x.get();
|
||||
//~^ WARN not reporting region error due to -Znll
|
||||
demand_y(x, y, p)
|
||||
//~^ ERROR free region `'_#5r` does not outlive free region `'_#6r`
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,46 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-approximated-fail-no-postdom.rs:55:21
|
||||
|
|
||||
55 | let p = x.get();
|
||||
| ^^^^^^^
|
||||
|
||||
error: free region `'_#5r` does not outlive free region `'_#6r`
|
||||
--> $DIR/propagate-approximated-fail-no-postdom.rs:57:25
|
||||
|
|
||||
57 | demand_y(x, y, p)
|
||||
| ^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-approximated-fail-no-postdom.rs:53:9
|
||||
|
|
||||
53 | / |_outlives1, _outlives2, _outlives3, x, y| {
|
||||
54 | | // Only works if 'x: 'y:
|
||||
55 | | let p = x.get();
|
||||
56 | | //~^ WARN not reporting region error due to -Znll
|
||||
57 | | demand_y(x, y, p)
|
||||
58 | | //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r`
|
||||
59 | | },
|
||||
| |_________^
|
||||
|
|
||||
= note: defining type: DefId(0/1:20 ~ propagate_approximated_fail_no_postdom[317d]::supply[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
|
||||
]
|
||||
= note: number of external vids: 4
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-approximated-fail-no-postdom.rs:48:1
|
||||
|
|
||||
48 | / fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) {
|
||||
49 | | establish_relationships(
|
||||
50 | | cell_a,
|
||||
51 | | cell_b,
|
||||
... |
|
||||
60 | | );
|
||||
61 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_approximated_fail_no_postdom[317d]::supply[0]) with substs []
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,64 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Rather convoluted setup where we infer a relationship between two
|
||||
// free regions in the closure signature (`'a` and `'b`) on the basis
|
||||
// of a relationship between two bound regions (`'x` and `'y`).
|
||||
//
|
||||
// The idea is that, thanks to invoking `demand_y`, `'x: 'y` must
|
||||
// hold, where `'x` and `'y` are bound regions. The closure can't
|
||||
// prove that directly, and because `'x` and `'y` are bound it cannot
|
||||
// ask the caller to prove it either. But it has bounds on `'x` and
|
||||
// `'y` in terms of `'a` and `'b`, and it can propagate a relationship
|
||||
// between `'a` and `'b` to the caller.
|
||||
//
|
||||
// Note: the use of `Cell` here is to introduce invariance. One less
|
||||
// variable.
|
||||
//
|
||||
// FIXME(#45827): The `supply` function *ought* to generate an error, but it
|
||||
// currently does not. This is I believe a shortcoming of the MIR type
|
||||
// checker: the closure inference is expressing the correct
|
||||
// requirement, as you can see from the `#[rustc_regions]` output.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// Callee knows that:
|
||||
//
|
||||
// 'x: 'a
|
||||
// 'b: 'y
|
||||
//
|
||||
// so if we are going to ensure that `'x: 'y`, then `'a: 'b` must
|
||||
// hold.
|
||||
fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
|
||||
where
|
||||
F: for<'x, 'y> FnMut(
|
||||
&Cell<&'a &'x u32>, // shows that 'x: 'a
|
||||
&Cell<&'y &'b u32>, // shows that 'b: 'y
|
||||
&Cell<&'x u32>,
|
||||
&Cell<&'y u32>,
|
||||
),
|
||||
{
|
||||
}
|
||||
|
||||
fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
|
||||
|
||||
#[rustc_regions]
|
||||
fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
// Only works if 'x: 'y:
|
||||
demand_y(x, y, x.get())
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,36 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-approximated-ref.rs:60:9
|
||||
|
|
||||
60 | demand_y(x, y, x.get())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-approximated-ref.rs:58:47
|
||||
|
|
||||
58 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
| _______________________________________________^
|
||||
59 | | // Only works if 'x: 'y:
|
||||
60 | | demand_y(x, y, x.get())
|
||||
61 | | });
|
||||
| |_____^
|
||||
|
|
||||
= note: defining type: DefId(0/1:18 ~ propagate_approximated_ref[317d]::supply[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>))
|
||||
]
|
||||
= note: number of external vids: 3
|
||||
= note: where '_#1r: '_#2r
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-approximated-ref.rs:57:1
|
||||
|
|
||||
57 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
58 | | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
59 | | // Only works if 'x: 'y:
|
||||
60 | | demand_y(x, y, x.get())
|
||||
61 | | });
|
||||
62 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_approximated_ref[317d]::supply[0]) with substs []
|
||||
|
@ -0,0 +1,53 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test a case where we setup relationships like `'x: 'a` or `'a: 'x`,
|
||||
// where `'x` is bound in closure type but `'a` is free. This forces
|
||||
// us to approximate `'x` one way or the other.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
fn foo<'a, F>(_cell: Cell<&'a u32>, _f: F)
|
||||
where
|
||||
F: for<'x> FnOnce(Cell<&'a u32>, Cell<&'x u32>),
|
||||
{
|
||||
}
|
||||
|
||||
#[rustc_regions]
|
||||
fn case1() {
|
||||
let a = 0;
|
||||
let cell = Cell::new(&a);
|
||||
foo(cell, |cell_a, cell_x| {
|
||||
//~^ WARNING not reporting region error due to -Znll
|
||||
cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
|
||||
//~^ ERROR free region `'_#2r` does not outlive free region `'_#1r`
|
||||
})
|
||||
}
|
||||
|
||||
#[rustc_regions]
|
||||
fn case2() {
|
||||
let a = 0;
|
||||
let cell = Cell::new(&a);
|
||||
|
||||
// As you can see in the stderr output, this closure propoagates a
|
||||
// requirement that `'a: 'static'.
|
||||
//
|
||||
// FIXME(#45827) However, because of shortcomings in the MIR type
|
||||
// checker, this does not result in errors later on (yet).
|
||||
foo(cell, |cell_a, cell_x| {
|
||||
cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error
|
||||
})
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,75 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:5
|
||||
|
|
||||
31 | foo(cell, |cell_a, cell_x| {
|
||||
| ^^^
|
||||
|
||||
error: free region `'_#2r` does not outlive free region `'_#1r`
|
||||
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:33:9
|
||||
|
|
||||
33 | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
|
||||
| ^^^^^^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15
|
||||
|
|
||||
31 | foo(cell, |cell_a, cell_x| {
|
||||
| _______________^
|
||||
32 | | //~^ WARNING not reporting region error due to -Znll
|
||||
33 | | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
|
||||
34 | | //~^ ERROR free region `'_#2r` does not outlive free region `'_#1r`
|
||||
35 | | })
|
||||
| |_____^
|
||||
|
|
||||
= note: defining type: DefId(0/1:12 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case1[0]::{{closure}}[0]) with closure substs [
|
||||
i32,
|
||||
for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>))
|
||||
]
|
||||
= note: number of external vids: 2
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:28:1
|
||||
|
|
||||
28 | / fn case1() {
|
||||
29 | | let a = 0;
|
||||
30 | | let cell = Cell::new(&a);
|
||||
31 | | foo(cell, |cell_a, cell_x| {
|
||||
... |
|
||||
35 | | })
|
||||
36 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:5 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case1[0]) with substs []
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:48:15
|
||||
|
|
||||
48 | foo(cell, |cell_a, cell_x| {
|
||||
| _______________^
|
||||
49 | | cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error
|
||||
50 | | })
|
||||
| |_____^
|
||||
|
|
||||
= note: defining type: DefId(0/1:13 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case2[0]::{{closure}}[0]) with closure substs [
|
||||
i32,
|
||||
for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>))
|
||||
]
|
||||
= note: number of external vids: 2
|
||||
= note: where '_#1r: '_#0r
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:39:1
|
||||
|
|
||||
39 | / fn case2() {
|
||||
40 | | let a = 0;
|
||||
41 | | let cell = Cell::new(&a);
|
||||
42 | |
|
||||
... |
|
||||
50 | | })
|
||||
51 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case2[0]) with substs []
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,51 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test a case where we are trying to prove `'x: 'y` and are forced to
|
||||
// approximate the shorter end-point (`'y`) to with `'static`. This is
|
||||
// because `'y` is higher-ranked but we know of no relations to other
|
||||
// regions. Note that `'static` shows up in the stderr output as `'0`.
|
||||
//
|
||||
// FIXME(#45827) Because of shortcomings in the MIR type checker,
|
||||
// these errors are not (yet) reported.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// Callee knows that:
|
||||
//
|
||||
// 'x: 'a
|
||||
//
|
||||
// so the only way we can ensure that `'x: 'y` is to show that
|
||||
// `'a: 'static`.
|
||||
fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
|
||||
where
|
||||
F: for<'x, 'y> FnMut(
|
||||
&Cell<&'a &'x u32>, // shows that 'x: 'a
|
||||
&Cell<&'x u32>,
|
||||
&Cell<&'y u32>,
|
||||
),
|
||||
{
|
||||
}
|
||||
|
||||
fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
|
||||
|
||||
#[rustc_regions]
|
||||
fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
|
||||
// Only works if 'x: 'y:
|
||||
demand_y(x, y, x.get())
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,36 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:47:9
|
||||
|
|
||||
47 | demand_y(x, y, x.get())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:47
|
||||
|
|
||||
45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
|
||||
| _______________________________________________^
|
||||
46 | | // Only works if 'x: 'y:
|
||||
47 | | demand_y(x, y, x.get())
|
||||
48 | | });
|
||||
| |_____^
|
||||
|
|
||||
= note: defining type: DefId(0/1:18 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) u32>))
|
||||
]
|
||||
= note: number of external vids: 2
|
||||
= note: where '_#1r: '_#0r
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:44:1
|
||||
|
|
||||
44 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
45 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
|
||||
46 | | // Only works if 'x: 'y:
|
||||
47 | | demand_y(x, y, x.get())
|
||||
48 | | });
|
||||
49 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]) with substs []
|
||||
|
@ -0,0 +1,54 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test a case where we are trying to prove `'x: 'y` and are forced to
|
||||
// approximate the shorter end-point (`'y`) to with `'static`. This is
|
||||
// because `'y` is higher-ranked but we know of only irrelevant
|
||||
// relations to other regions. Note that `'static` shows up in the
|
||||
// stderr output as `'0`.
|
||||
//
|
||||
// FIXME(#45827) Because of shortcomings in the MIR type checker,
|
||||
// these errors are not (yet) reported.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// Callee knows that:
|
||||
//
|
||||
// 'x: 'a
|
||||
// 'y: 'b
|
||||
//
|
||||
// so the only way we can ensure that `'x: 'y` is to show that
|
||||
// `'a: 'static`.
|
||||
fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
|
||||
where
|
||||
F: for<'x, 'y> FnMut(
|
||||
&Cell<&'a &'x u32>, // shows that 'x: 'a
|
||||
&Cell<&'b &'y u32>, // shows that 'y: 'b
|
||||
&Cell<&'x u32>,
|
||||
&Cell<&'y u32>,
|
||||
),
|
||||
{
|
||||
}
|
||||
|
||||
fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
|
||||
|
||||
#[rustc_regions]
|
||||
fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
// Only works if 'x: 'y:
|
||||
demand_y(x, y, x.get())
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,36 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:50:9
|
||||
|
|
||||
50 | demand_y(x, y, x.get())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:47
|
||||
|
|
||||
48 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
| _______________________________________________^
|
||||
49 | | // Only works if 'x: 'y:
|
||||
50 | | demand_y(x, y, x.get())
|
||||
51 | | });
|
||||
| |_____^
|
||||
|
|
||||
= note: defining type: DefId(0/1:18 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>))
|
||||
]
|
||||
= note: number of external vids: 3
|
||||
= note: where '_#1r: '_#0r
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:47:1
|
||||
|
|
||||
47 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
48 | | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
49 | | // Only works if 'x: 'y:
|
||||
50 | | demand_y(x, y, x.get())
|
||||
51 | | });
|
||||
52 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]) with substs []
|
||||
|
@ -0,0 +1,52 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// A simpler variant of `outlives-from-argument` where cells are
|
||||
// passed by value.
|
||||
//
|
||||
// This is simpler because there are no "extraneous" region
|
||||
// relationships. In the 'main' variant, there are a number of
|
||||
// anonymous regions as well.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// Callee knows that:
|
||||
//
|
||||
// 'x: 'a
|
||||
// 'b: 'y
|
||||
//
|
||||
// so if we are going to ensure that `'x: 'y`, then `'a: 'b` must
|
||||
// hold.
|
||||
fn establish_relationships<'a, 'b, F>(_cell_a: Cell<&'a u32>, _cell_b: Cell<&'b u32>, _closure: F)
|
||||
where
|
||||
F: for<'x, 'y> FnMut(
|
||||
Cell<&'a &'x u32>, // shows that 'x: 'a
|
||||
Cell<&'y &'b u32>, // shows that 'b: 'y
|
||||
Cell<&'x u32>,
|
||||
Cell<&'y u32>,
|
||||
),
|
||||
{
|
||||
}
|
||||
|
||||
fn demand_y<'x, 'y>(_outlives1: Cell<&&'x u32>, _outlives2: Cell<&'y &u32>, _y: &'y u32) {}
|
||||
|
||||
#[rustc_regions]
|
||||
fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
|
||||
// Only works if 'x: 'y:
|
||||
demand_y(outlives1, outlives2, x.get())
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,36 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-approximated-val.rs:48:9
|
||||
|
|
||||
48 | demand_y(outlives1, outlives2, x.get())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-approximated-val.rs:46:45
|
||||
|
|
||||
46 | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
|
||||
| _____________________________________________^
|
||||
47 | | // Only works if 'x: 'y:
|
||||
48 | | demand_y(outlives1, outlives2, x.get())
|
||||
49 | | });
|
||||
| |_____^
|
||||
|
|
||||
= note: defining type: DefId(0/1:18 ~ propagate_approximated_val[317d]::test[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
|
||||
]
|
||||
= note: number of external vids: 3
|
||||
= note: where '_#1r: '_#2r
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-approximated-val.rs:45:1
|
||||
|
|
||||
45 | / fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
46 | | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| {
|
||||
47 | | // Only works if 'x: 'y:
|
||||
48 | | demand_y(outlives1, outlives2, x.get())
|
||||
49 | | });
|
||||
50 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_approximated_val[317d]::test[0]) with substs []
|
||||
|
@ -0,0 +1,59 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test where we might in theory be able to see that the relationship
|
||||
// between two bound regions is true within closure and hence have no
|
||||
// need to propagate; but in fact we do because identity of free
|
||||
// regions is erased.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// In theory, callee knows that:
|
||||
//
|
||||
// 'x: 'a
|
||||
// 'a: 'y
|
||||
//
|
||||
// and hence could satisfy that `'x: 'y` locally. However, in our
|
||||
// checking, we ignore the precise free regions that come into the
|
||||
// region and just assign each position a distinct universally bound
|
||||
// region. Hence, we propagate a constraint to our caller that will
|
||||
// wind up being solvable.
|
||||
fn establish_relationships<'a, F>(
|
||||
_cell_a: Cell<&'a u32>,
|
||||
_closure: F,
|
||||
) where
|
||||
F: for<'x, 'y> FnMut(
|
||||
Cell<&'a &'x u32>, // shows that 'x: 'a
|
||||
Cell<&'y &'a u32>, // shows that 'a: 'y
|
||||
Cell<&'x u32>,
|
||||
Cell<&'y u32>,
|
||||
),
|
||||
{
|
||||
}
|
||||
|
||||
fn demand_y<'x, 'y>(_cell_x: Cell<&'x u32>, _cell_y: Cell<&'y u32>, _y: &'y u32) {}
|
||||
|
||||
#[rustc_regions]
|
||||
fn supply<'a>(cell_a: Cell<&'a u32>) {
|
||||
establish_relationships(
|
||||
cell_a,
|
||||
|_outlives1, _outlives2, x, y| {
|
||||
// Only works if 'x: 'y:
|
||||
let p = x.get();
|
||||
demand_y(x, y, p)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,37 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-despite-same-free-region.rs:53:21
|
||||
|
|
||||
53 | let p = x.get();
|
||||
| ^^^^^^^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-despite-same-free-region.rs:51:9
|
||||
|
|
||||
51 | / |_outlives1, _outlives2, x, y| {
|
||||
52 | | // Only works if 'x: 'y:
|
||||
53 | | let p = x.get();
|
||||
54 | | demand_y(x, y, p)
|
||||
55 | | },
|
||||
| |_________^
|
||||
|
|
||||
= note: defining type: DefId(0/1:16 ~ propagate_despite_same_free_region[317d]::supply[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
|
||||
]
|
||||
= note: number of external vids: 3
|
||||
= note: where '_#1r: '_#2r
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-despite-same-free-region.rs:48:1
|
||||
|
|
||||
48 | / fn supply<'a>(cell_a: Cell<&'a u32>) {
|
||||
49 | | establish_relationships(
|
||||
50 | | cell_a,
|
||||
51 | | |_outlives1, _outlives2, x, y| {
|
||||
... |
|
||||
56 | | );
|
||||
57 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_despite_same_free_region[317d]::supply[0]) with substs []
|
||||
|
@ -0,0 +1,53 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Similarly to escape-argument-callee, a test case where the closure
|
||||
// requires a relationship between 2 unrelated higher-ranked regions,
|
||||
// with no helpful relations between the HRRs and free regions.
|
||||
//
|
||||
// In this case, the error is reported by the closure itself. This is
|
||||
// because it is unable to approximate the higher-ranked region `'x`,
|
||||
// as it knows of no relationships between `'x` and any
|
||||
// non-higher-ranked regions.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// Callee knows that:
|
||||
//
|
||||
// 'b: 'y
|
||||
//
|
||||
// but this doesn't really help us in proving that `'x: 'y`, so closure gets an error.
|
||||
fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
|
||||
where
|
||||
F: for<'x, 'y> FnMut(
|
||||
&Cell<&'y &'b u32>, // shows that 'b: 'y
|
||||
&Cell<&'x u32>,
|
||||
&Cell<&'y u32>,
|
||||
),
|
||||
{
|
||||
}
|
||||
|
||||
fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
|
||||
|
||||
#[rustc_regions]
|
||||
fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
|
||||
// Only works if 'x: 'y:
|
||||
demand_y(x, y, x.get())
|
||||
//~^ WARN not reporting region error due to -Znll
|
||||
//~| ERROR free region `'_#6r` does not outlive free region `'_#4r`
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,46 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:9
|
||||
|
|
||||
47 | demand_y(x, y, x.get())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: free region `'_#6r` does not outlive free region `'_#4r`
|
||||
--> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:21
|
||||
|
|
||||
47 | demand_y(x, y, x.get())
|
||||
| ^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47
|
||||
|
|
||||
45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
|
||||
| _______________________________________________^
|
||||
46 | | // Only works if 'x: 'y:
|
||||
47 | | demand_y(x, y, x.get())
|
||||
48 | | //~^ WARN not reporting region error due to -Znll
|
||||
49 | | //~| ERROR free region `'_#6r` does not outlive free region `'_#4r`
|
||||
50 | | });
|
||||
| |_____^
|
||||
|
|
||||
= note: defining type: DefId(0/1:18 ~ propagate_fail_to_approximate_longer_no_bounds[317d]::supply[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
|
||||
]
|
||||
= note: number of external vids: 2
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:44:1
|
||||
|
|
||||
44 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
45 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
|
||||
46 | | // Only works if 'x: 'y:
|
||||
47 | | demand_y(x, y, x.get())
|
||||
... |
|
||||
50 | | });
|
||||
51 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_fail_to_approximate_longer_no_bounds[317d]::supply[0]) with substs []
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,57 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Similarly to escape-argument-callee, a test case where the closure
|
||||
// requires a relationship between 2 unrelated higher-ranked regions,
|
||||
// with no helpful relations between the HRRs and free regions.
|
||||
//
|
||||
// In this case, the error is reported by the closure itself. This is
|
||||
// because it is unable to approximate the higher-ranked region `'x`,
|
||||
// as it only knows of regions that `'x` is outlived by, and none that
|
||||
// `'x` outlives.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// Callee knows that:
|
||||
//
|
||||
// 'a: 'x
|
||||
// 'b: 'y
|
||||
//
|
||||
// but this doesn't really help us in proving that `'x: 'y`, so
|
||||
// closure gets an error. In particular, we would need to know that
|
||||
// `'x: 'a`, so that we could approximate `'x` "downwards" to `'a`.
|
||||
fn establish_relationships<'a, 'b, F>(_cell_a: &Cell<&'a u32>, _cell_b: &Cell<&'b u32>, _closure: F)
|
||||
where
|
||||
F: for<'x, 'y> FnMut(
|
||||
&Cell<&'x &'a u32>, // shows that 'a: 'x
|
||||
&Cell<&'y &'b u32>, // shows that 'b: 'y
|
||||
&Cell<&'x u32>,
|
||||
&Cell<&'y u32>,
|
||||
),
|
||||
{
|
||||
}
|
||||
|
||||
fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u32) {}
|
||||
|
||||
#[rustc_regions]
|
||||
fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
// Only works if 'x: 'y:
|
||||
demand_y(x, y, x.get())
|
||||
//~^ WARN not reporting region error due to -Znll
|
||||
//~| ERROR free region `'_#5r` does not outlive free region `'_#7r`
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,46 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:9
|
||||
|
|
||||
51 | demand_y(x, y, x.get())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: free region `'_#5r` does not outlive free region `'_#7r`
|
||||
--> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:21
|
||||
|
|
||||
51 | demand_y(x, y, x.get())
|
||||
| ^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47
|
||||
|
|
||||
49 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
| _______________________________________________^
|
||||
50 | | // Only works if 'x: 'y:
|
||||
51 | | demand_y(x, y, x.get())
|
||||
52 | | //~^ WARN not reporting region error due to -Znll
|
||||
53 | | //~| ERROR free region `'_#5r` does not outlive free region `'_#7r`
|
||||
54 | | });
|
||||
| |_____^
|
||||
|
|
||||
= note: defining type: DefId(0/1:18 ~ propagate_fail_to_approximate_longer_wrong_bounds[317d]::supply[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>))
|
||||
]
|
||||
= note: number of external vids: 3
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:48:1
|
||||
|
|
||||
48 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
|
||||
49 | | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
|
||||
50 | | // Only works if 'x: 'y:
|
||||
51 | | demand_y(x, y, x.get())
|
||||
... |
|
||||
54 | | });
|
||||
55 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:6 ~ propagate_fail_to_approximate_longer_wrong_bounds[317d]::supply[0]) with substs []
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,24 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Basic test for free regions in the NLL code. This test ought to
|
||||
// report an error due to a reborrowing constraint. Right now, we get
|
||||
// a variety of errors from the older, AST-based machinery (notably
|
||||
// borrowck), and then we get the NLL error at the end.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
fn foo(x: &u32) -> &'static u32 {
|
||||
&*x
|
||||
//~^ WARN not reporting region error due to -Znll
|
||||
//~| ERROR free region `'_#1r` does not outlive free region `ReStatic`
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,14 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:5
|
||||
|
|
||||
19 | &*x
|
||||
| ^^^
|
||||
|
||||
error: free region `'_#1r` does not outlive free region `ReStatic`
|
||||
--> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:5
|
||||
|
|
||||
19 | &*x
|
||||
| ^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,24 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Basic test for free regions in the NLL code. This test ought to
|
||||
// report an error due to a reborrowing constraint. Right now, we get
|
||||
// a variety of errors from the older, AST-based machinery (notably
|
||||
// borrowck), and then we get the NLL error at the end.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
fn foo<'a>(x: &'a u32) -> &'static u32 {
|
||||
&*x
|
||||
//~^ WARN not reporting region error due to -Znll
|
||||
//~| ERROR free region `'_#1r` does not outlive free region `ReStatic`
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,14 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/region-lbr-named-does-not-outlive-static.rs:19:5
|
||||
|
|
||||
19 | &*x
|
||||
| ^^^
|
||||
|
||||
error: free region `'_#1r` does not outlive free region `ReStatic`
|
||||
--> $DIR/region-lbr-named-does-not-outlive-static.rs:19:5
|
||||
|
|
||||
19 | &*x
|
||||
| ^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -13,12 +13,12 @@
|
||||
// a variety of errors from the older, AST-based machinery (notably
|
||||
// borrowck), and then we get the NLL error at the end.
|
||||
|
||||
// compile-flags:-Znll
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||
&*x //~ ERROR free region `'a` does not outlive `'b`
|
||||
//~^ ERROR `*x` does not live long enough
|
||||
//~| WARN not reporting region error due to -Znll
|
||||
&*x
|
||||
//~^ WARN not reporting region error due to -Znll
|
||||
//~| ERROR free region `'_#1r` does not outlive free region `'_#2r`
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,14 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/region-lbr1-does-not-outlive-ebr2.rs:19:5
|
||||
|
|
||||
19 | &*x
|
||||
| ^^^
|
||||
|
||||
error: free region `'_#1r` does not outlive free region `'_#2r`
|
||||
--> $DIR/region-lbr1-does-not-outlive-ebr2.rs:19:5
|
||||
|
|
||||
19 | &*x
|
||||
| ^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,23 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Basic test for free regions in the NLL code. This test does not
|
||||
// report an error because of the (implied) bound that `'b: 'a`.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
// must-compile-successfully
|
||||
|
||||
#![allow(warnings)]
|
||||
|
||||
fn foo<'a, 'b>(x: &'a &'b u32) -> &'a u32 {
|
||||
&**x
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,34 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test closure that takes two references and is supposed to return
|
||||
// the first, but actually returns the second. This should fail within
|
||||
// the closure.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck=mir -Zverbose
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_regions]
|
||||
fn test() {
|
||||
expect_sig(|a, b| b); // ought to return `a`
|
||||
//~^ WARN not reporting region error due to -Znll
|
||||
//~| ERROR free region `'_#3r` does not outlive free region `'_#2r`
|
||||
}
|
||||
|
||||
fn expect_sig<F>(f: F) -> F
|
||||
where F: for<'a> FnMut(&'a i32, &i32) -> &'a i32
|
||||
{
|
||||
f
|
||||
}
|
||||
|
||||
fn deref(_p: &i32) { }
|
||||
|
||||
fn main() { }
|
@ -0,0 +1,38 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/return-wrong-bound-region.rs:21:23
|
||||
|
|
||||
21 | expect_sig(|a, b| b); // ought to return `a`
|
||||
| ^
|
||||
|
||||
error: free region `'_#3r` does not outlive free region `'_#2r`
|
||||
--> $DIR/return-wrong-bound-region.rs:21:23
|
||||
|
|
||||
21 | expect_sig(|a, b| b); // ought to return `a`
|
||||
| ^
|
||||
|
||||
note: External requirements
|
||||
--> $DIR/return-wrong-bound-region.rs:21:16
|
||||
|
|
||||
21 | expect_sig(|a, b| b); // ought to return `a`
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: defining type: DefId(0/1:9 ~ return_wrong_bound_region[317d]::test[0]::{{closure}}[0]) with closure substs [
|
||||
i16,
|
||||
for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) -> &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32
|
||||
]
|
||||
= note: number of external vids: 1
|
||||
|
||||
note: No external requirements
|
||||
--> $DIR/return-wrong-bound-region.rs:20:1
|
||||
|
|
||||
20 | / fn test() {
|
||||
21 | | expect_sig(|a, b| b); // ought to return `a`
|
||||
22 | | //~^ WARN not reporting region error due to -Znll
|
||||
23 | | //~| ERROR free region `'_#3r` does not outlive free region `'_#2r`
|
||||
24 | | }
|
||||
| |_^
|
||||
|
|
||||
= note: defining type: DefId(0/0:3 ~ return_wrong_bound_region[317d]::test[0]) with substs []
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,31 +0,0 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/named-region-basic.rs:19:5
|
||||
|
|
||||
19 | &*x //~ ERROR free region `'a` does not outlive `'b`
|
||||
| ^^^
|
||||
|
||||
error[E0597]: `*x` does not live long enough
|
||||
--> $DIR/named-region-basic.rs:19:6
|
||||
|
|
||||
19 | &*x //~ ERROR free region `'a` does not outlive `'b`
|
||||
| ^^ does not live long enough
|
||||
|
|
||||
= note: borrowed value must be valid for the static lifetime...
|
||||
note: ...but borrowed value is only valid for the lifetime 'a as defined on the function body at 18:1
|
||||
--> $DIR/named-region-basic.rs:18:1
|
||||
|
|
||||
18 | / fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||
19 | | &*x //~ ERROR free region `'a` does not outlive `'b`
|
||||
20 | | //~^ ERROR `*x` does not live long enough
|
||||
21 | | //~| WARN not reporting region error due to -Znll
|
||||
22 | | }
|
||||
| |_^
|
||||
|
||||
error: free region `'a` does not outlive `'b`
|
||||
--> $DIR/named-region-basic.rs:19:5
|
||||
|
|
||||
19 | &*x //~ ERROR free region `'a` does not outlive `'b`
|
||||
| ^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -2535,7 +2535,10 @@ impl<'test> TestCx<'test> {
|
||||
let mut dumped_file = fs::File::open(output_file.clone()).unwrap();
|
||||
let mut dumped_string = String::new();
|
||||
dumped_file.read_to_string(&mut dumped_string).unwrap();
|
||||
let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty());
|
||||
let mut dumped_lines = dumped_string
|
||||
.lines()
|
||||
.map(|l| nocomment_mir_line(l))
|
||||
.filter(|l| !l.is_empty());
|
||||
let mut expected_lines = expected_content
|
||||
.iter()
|
||||
.filter(|&l| {
|
||||
@ -2573,7 +2576,7 @@ impl<'test> TestCx<'test> {
|
||||
.join("\n");
|
||||
panic!(
|
||||
"Did not find expected line, error: {}\n\
|
||||
Actual Line: {:?}\n\
|
||||
Expected Line: {:?}\n\
|
||||
Expected:\n{}\n\
|
||||
Actual:\n{}",
|
||||
extra_msg,
|
||||
@ -2599,7 +2602,9 @@ impl<'test> TestCx<'test> {
|
||||
error(
|
||||
expected_line,
|
||||
format!(
|
||||
"Mismatch in lines\nCurrnt block: {}\nExpected Line: {:?}",
|
||||
"Mismatch in lines\n\
|
||||
Current block: {}\n\
|
||||
Actual Line: {:?}",
|
||||
start_block_line.unwrap_or("None"),
|
||||
dumped_line
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user