diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 331b44ac119c6..beee34e11b724 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -572,3 +572,15 @@ impl<'gcx> HashStable> 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 +}); + diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 9609ae5a0beb8..2655e2acbbdfb 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -84,6 +84,16 @@ for ty::RegionKind { } } +impl<'gcx> HashStable> for ty::RegionVid { + #[inline] + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + use rustc_data_structures::indexed_vec::Idx; + self.index().hash_stable(hcx, hasher); + } +} + impl<'gcx> HashStable> for ty::adjustment::AutoBorrow<'gcx> { fn hash_stable(&self, diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 96a980a15457e..f5595d07340bb 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -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 diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index c803e76aebea4..720d831a24533 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1789,6 +1789,75 @@ pub struct GeneratorLayout<'tcx> { pub fields: Vec>, } +/// 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, +} + +/// 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 */ diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 429771cca9844..3342d13dd6e5f 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -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) diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 0c4071b8b5d9c..e70de0e566e41 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -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) diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 70636f8b6fe9b..177c25ac5dba0 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -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([ diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index fb3600182d8a6..848d2a0a7def7 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -190,8 +190,10 @@ define_maps! { <'tcx> [] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (), [] fn borrowck: BorrowCheck(DefId) -> Rc, - // 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, /// Gets a complete map from all types to their inherent impls. /// Not meant to be used directly outside of coherence. diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 05aab27dc2acc..4f733b8f68a36 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -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(pub T); impl Binder { + /// 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 + 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 Binder { 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(self, u: Binder, f: F) -> Binder + 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(self, f: F) -> (Binder, Binder) + 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> { self.map_bound_ref(|fn_sig| fn_sig.inputs()[index]) } + pub fn inputs_and_output(&self) -> ty::Binder<&'tcx Slice>> { + self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output) + } pub fn output(&self) -> ty::Binder> { self.map_bound_ref(|fn_sig| fn_sig.output().clone()) } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index b1e9bc7e47c76..b0f61e9a19177 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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", diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 8d3491bd1d988..97d8a677fe8f1 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -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 { 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 { 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)] diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 804f5e2687597..4ff299efb9555 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -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>, move_data: &MoveData<'tcx>, -) -> RegionInferenceContext<'tcx> { +) -> ( + RegionInferenceContext<'tcx>, + Option, +) { // 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, ) { 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, +) { + 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; diff --git a/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs b/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs new file mode 100644 index 0000000000000..906efaef887c3 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs @@ -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 or the MIT license +// , 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 + )); + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs new file mode 100644 index 0000000000000..5477308bde948 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs @@ -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 or the MIT license +// , 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(()) + } +} + diff --git a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs new file mode 100644 index 0000000000000..7923b159d80dc --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs @@ -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 or the MIT license +// , 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> { + 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 = 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 + } +} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index d1faaf75a5323..b2e2ccc5d0b72 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -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, - /// 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>, + /// 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>, } #[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 { 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); + + // 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); + } - for (fr, fr_definition) in free_region_definitions { - self.check_free_region(infcx, fr, fr_definition); + // 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, ) { 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(); - - // 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 - ), + debug!( + "check_universal_region: fr={:?} does not outlive shorter_fr={:?}", + longer_fr, + shorter_fr, + ); + + 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::>(); 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); + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 371419da02448..1262c238a132c 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -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>, 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(&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(&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={:?})", diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs index dbae40be6fe1d..c98a94fa8bc10 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -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) } } } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 3be95a114c3bc..857a620cead65 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -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, RegionVid>, - - /// The map from the typeck tables telling us how to relate universal regions. - pub free_region_map: &'tcx FreeRegionMap<'tcx>, + indices: UniversalRegionIndices<'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, +} + +#[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, RegionVid>, +} + +#[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, + + /// 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, +} + +#[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, } -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); +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> { + 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" + ); - let mut indices = FxHashMap(); + region_mapping + } - // `'static` is always free. - insert_free_region(&mut indices, infcx.tcx.types.re_static); + /// 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()) + } - // 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); + /// 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 { + 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 { + (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 { + 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, + fr0: RegionVid, + ) -> Option { + 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)); + } + + 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, 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) + } +} + +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>> { + 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]), + }; - UniversalRegions { indices, free_region_map: &tables.free_region_map } + 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(&mut self, indices: &UniversalRegionIndices<'tcx>, outlives_bounds: I) + where + I: IntoIterator>, + { + 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( + &self, + origin: NLLRegionVariableOrigin, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx>; + + fn replace_bound_regions_with_nll_infer_vars( + &self, + origin: NLLRegionVariableOrigin, + value: &ty::Binder, + ) -> T + where + T: TypeFoldable<'tcx>; +} + +impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> { + fn replace_free_regions_with_nll_infer_vars( + &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( + &self, + origin: NLLRegionVariableOrigin, + value: &ty::Binder, + ) -> T + where + T: TypeFoldable<'tcx>, + { + let (value, _map) = self.tcx + .replace_late_bound_regions(value, |_br| self.next_nll_region_var(origin)); + value + } } -fn insert_free_region<'tcx>( - universal_regions: &mut FxHashMap, RegionVid>, - region: ty::Region<'tcx>, -) { - let next = RegionVid::new(universal_regions.len()); - universal_regions.entry(region).or_insert(next); +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(&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))), + ) + } } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index f0b62e28a0da6..1a74f32700151 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -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, diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 8a3db0eb25b99..37f59773cd6f0 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -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 `` 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( - 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, +} + +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 {}", diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 405647af324d6..762cf7a0055d5 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -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( diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 764021983e99c..0799a388a8bec 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -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)); diff --git a/src/librustc_trans_utils/monomorphize.rs b/src/librustc_trans_utils/monomorphize.rs index 66833a1a7c2f1..d586d1ac31506 100644 --- a/src/librustc_trans_utils/monomorphize.rs +++ b/src/librustc_trans_utils/monomorphize.rs @@ -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([ diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 8f409b687526b..df1694a601056 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -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) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 3e725d7ef415c..dc5d3141d4ca2 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -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, _) => { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b754c981b2101..85b926a707db3 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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 => { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index e5296e7d88cd6..22cef25320ed3 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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 \ diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 69ddd56021377..7276036071103 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -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, diff --git a/src/test/compile-fail/regions-static-bound.rs b/src/test/compile-fail/regions-static-bound.rs index de695e72d07e4..9de8ca196f8ce 100644 --- a/src/test/compile-fail/regions-static-bound.rs +++ b/src/test/compile-fail/regions-static-bound.rs @@ -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() {} diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs index e4dd99f5a1e75..09288cf69ff58 100644 --- a/src/test/mir-opt/nll/liveness-call-subtlety.rs +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -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 >::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 diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs index 8dae773806718..b060222a95f17 100644 --- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -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 diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs index 5d799d3d90b41..671f5e5292aa4 100644 --- a/src/test/mir-opt/nll/liveness-interblock.rs +++ b/src/test/mir-opt/nll/liveness-interblock.rs @@ -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 diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 7039de727faa9..0c42585a528e0 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -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 diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index f51e839e4fc35..d203472f20c73 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -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; diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index cfbc51f9e1861..c04cedbc04b4d 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -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 diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs index 04a30dc284d77..e2ad49a443625 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -44,5 +44,5 @@ unsafe impl<#[may_dangle] T> Drop for Wrap { // 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 diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs index 5569fe7f5748c..e0272a51d03d9 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -46,5 +46,5 @@ impl Drop for Wrap { // 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 diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index 679f31fdab903..8aa0eb1a3a90e 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -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; // ... diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 471d77aefac62..2bc165bd3c4a3 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -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; diff --git a/src/test/ui/nll/capture-ref-in-struct.rs b/src/test/ui/nll/capture-ref-in-struct.rs new file mode 100644 index 0000000000000..00a0c94d2218a --- /dev/null +++ b/src/test/ui/nll/capture-ref-in-struct.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr new file mode 100644 index 0000000000000..f10e52e05f19d --- /dev/null +++ b/src/test/ui/nll/capture-ref-in-struct.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.rs b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs new file mode 100644 index 0000000000000..1e34aaf1ea030 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs @@ -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 or the MIT license +// , 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 + where F: FnMut(&mut &i32, &i32) +{ + f +} + +fn deref(_p: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr new file mode 100644 index 0000000000000..c842d51a2ad74 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/escape-argument.rs b/src/test/ui/nll/closure-requirements/escape-argument.rs new file mode 100644 index 0000000000000..1d8a916345e22 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument.rs @@ -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 or the MIT license +// , 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 + where F: for<'a, 'b> FnMut(&'a mut &'b i32, &'b i32) +{ + f +} + +fn deref(_p: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr new file mode 100644 index 0000000000000..e5c7139573eb9 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs new file mode 100644 index 0000000000000..9f4585bfbab8e --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr new file mode 100644 index 0000000000000..201590f01f38e --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs new file mode 100644 index 0000000000000..548a5ae59690c --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr new file mode 100644 index 0000000000000..47ba66ade6533 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs new file mode 100644 index 0000000000000..c2f071cc029e6 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr new file mode 100644 index 0000000000000..d581622c4c63e --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs new file mode 100644 index 0000000000000..76a0762461a2b --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr new file mode 100644 index 0000000000000..7553ac5b0c305 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -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 [] + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs new file mode 100644 index 0000000000000..0a47ee80256b5 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr new file mode 100644 index 0000000000000..e2de72ffe933f --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs new file mode 100644 index 0000000000000..f776ddc8b153f --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr new file mode 100644 index 0000000000000..8d1b9a94ea6a1 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -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 [] + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs new file mode 100644 index 0000000000000..54b501c9ab6be --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr new file mode 100644 index 0000000000000..9d318a63d8fcb --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -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 [] + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs new file mode 100644 index 0000000000000..48d446b00af8b --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr new file mode 100644 index 0000000000000..ae5ad6f4b9602 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -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 [] + diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs new file mode 100644 index 0000000000000..a28b5f4c0f949 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr new file mode 100644 index 0000000000000..64f3bb08c620c --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr @@ -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 [] + diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs new file mode 100644 index 0000000000000..4bbdcc4494486 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr new file mode 100644 index 0000000000000..6094f9aad81da --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs new file mode 100644 index 0000000000000..69fad354792f0 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr new file mode 100644 index 0000000000000..6658ee63abd9d --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs new file mode 100644 index 0000000000000..c61cf8a940f95 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr new file mode 100644 index 0000000000000..ef7ea9239127b --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs new file mode 100644 index 0000000000000..fcda5c5420bfe --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr new file mode 100644 index 0000000000000..6dcb8e7cf122d --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr @@ -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 + diff --git a/src/test/ui/nll/named-region-basic.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs similarity index 80% rename from src/test/ui/nll/named-region-basic.rs rename to src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs index 001ce41c27793..c1e4dee00656b 100644 --- a/src/test/ui/nll/named-region-basic.rs +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs @@ -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() { } diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr new file mode 100644 index 0000000000000..c1b2f44030949 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr @@ -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 + diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs new file mode 100644 index 0000000000000..ffb1935e75e32 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs new file mode 100644 index 0000000000000..9314bbf943222 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs @@ -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 or the MIT license +// , 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 + where F: for<'a> FnMut(&'a i32, &i32) -> &'a i32 +{ + f +} + +fn deref(_p: &i32) { } + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr new file mode 100644 index 0000000000000..8999f69e8ded6 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -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 + diff --git a/src/test/ui/nll/named-region-basic.stderr b/src/test/ui/nll/named-region-basic.stderr deleted file mode 100644 index 9c1de6c366cc9..0000000000000 --- a/src/test/ui/nll/named-region-basic.stderr +++ /dev/null @@ -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 - diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 91d51d359ecec..a18f4ec1aadb4 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -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 ),