From e5dc4ba2808ac81d7b3a1abab303e6d9eda5d9d6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Nov 2017 17:29:55 -0500 Subject: [PATCH 01/21] renumber types in `ty::Const` and relate them to `mir::Constant` --- src/librustc_mir/borrow_check/nll/mod.rs | 5 ++ src/librustc_mir/borrow_check/nll/renumber.rs | 5 ++ .../nll/subtype_constraint_generation.rs | 5 +- src/librustc_mir/transform/type_check.rs | 47 +++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 804f5e2687597..6d9e24bbb876f 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -43,12 +43,17 @@ pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( def_id: DefId, mir: &mut Mir<'tcx>, ) -> UniversalRegions<'tcx> { + debug!("replace_regions_in_mir(def_id={:?})", def_id); + // Compute named region information. let universal_regions = universal_regions::universal_regions(infcx, def_id); // Replace all 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 } diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 371419da02448..bb32cf88c7514 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -138,6 +138,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..e93f29f9bc8c5 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -106,7 +106,10 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { if let ty::ReVar(vid) = r { *vid } else { - self.universal_regions.indices[&r] + *self.universal_regions + .indices + .get(&r) + .unwrap_or_else(|| bug!("to_region_vid: bad region {:?}", r)) } } } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index f0b62e28a0da6..f24aa51eb2584 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -110,6 +110,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 +160,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>, From 2ec959fc35a8b0bdab13c03fcce9c90a929e9bd7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Nov 2017 17:33:26 -0500 Subject: [PATCH 02/21] extend MIR dump with detailed, extra information --- src/librustc_mir/util/pretty.rs | 117 ++++++++++++++++++++++----- src/tools/compiletest/src/runtest.rs | 11 ++- 2 files changed, 104 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 8a3db0eb25b99..98d746952a402 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; @@ -125,14 +126,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 +142,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 +285,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 +318,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 +339,93 @@ 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/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 ), From ab1c1bc6bc18b70818206e7f07ac5133239607ff Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Nov 2017 17:38:51 -0500 Subject: [PATCH 03/21] mir-borrowck returns closure requirements, mir-typeck enforces --- src/librustc/ich/impls_mir.rs | 12 + src/librustc/ich/impls_ty.rs | 10 + src/librustc/infer/mod.rs | 5 + src/librustc/mir/mod.rs | 69 ++ src/librustc/ty/maps/mod.rs | 6 +- src/librustc/ty/sty.rs | 40 + src/librustc_driver/driver.rs | 2 +- src/librustc_mir/borrow_check/mod.rs | 29 +- src/librustc_mir/borrow_check/nll/mod.rs | 31 +- .../borrow_check/nll/region_infer/mod.rs | 301 ++++++-- src/librustc_mir/borrow_check/nll/renumber.rs | 61 +- .../nll/subtype_constraint_generation.rs | 9 +- .../borrow_check/nll/universal_regions.rs | 694 ++++++++++++++++-- src/librustc_mir/transform/type_check.rs | 36 +- 14 files changed, 1121 insertions(+), 184 deletions(-) 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..0d4d294ad36f3 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> { 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/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..24cf14419e4ae 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -646,6 +646,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 new_not_binding<'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 +711,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 +836,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 6d9e24bbb876f..3d698abc83f43 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -9,7 +9,7 @@ // 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; @@ -35,20 +35,21 @@ 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> { debug!("replace_regions_in_mir(def_id={:?})", def_id); - // Compute named region information. - let universal_regions = universal_regions::universal_regions(infcx, def_id); + // Compute named region information. This also renumbers the inputs/outputs. + let universal_regions = UniversalRegions::new(infcx, def_id, param_env); - // Replace all regions with fresh inference variables. + // Replace all remaining regions with fresh inference variables. renumber::renumber_mir(infcx, &universal_regions, mir); let source = MirSource::item(def_id); @@ -68,7 +69,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); @@ -76,13 +80,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 { @@ -115,13 +114,13 @@ 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); - regioncx + (regioncx, closure_region_requirements) } struct LivenessResults { @@ -197,7 +196,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( /// 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/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index d1faaf75a5323..171deb3e1d753 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; @@ -52,12 +53,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 +65,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 +102,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 +137,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 +162,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 +198,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 +247,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 +305,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 +332,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 +521,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 +562,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 +633,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 +653,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 bb32cf88c7514..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); } 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 e93f29f9bc8c5..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,10 +102,7 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { if let ty::ReVar(vid) = r { *vid } else { - *self.universal_regions - .indices - .get(&r) - .unwrap_or_else(|| bug!("to_region_vid: bad region {:?}", 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..35c50f941907a 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -22,69 +22,671 @@ //! 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> { + let param_env = self.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(); + + let indices = self.compute_indices(fr_static, defining_ty); + + 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 { + 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) { + 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::new_not_binding(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>) { + 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 { + 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))); + } + } + } + } } -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 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 + } +} + +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 f24aa51eb2584..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}; @@ -1135,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, From 05441abd2bc1f0658aafa8b0161146fae34f7206 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Nov 2017 17:39:46 -0500 Subject: [PATCH 04/21] add closure requirement tests, improve debugging output The overall format is now easier to read. Also, There is now graphviz output, as well as a `#[rustc_regions]` annotation that dumps internal state. --- src/librustc_mir/borrow_check/nll/mod.rs | 111 +++++++++++++++++- .../nll/region_infer/annotation.rs | 48 ++++++++ .../borrow_check/nll/region_infer/dump_mir.rs | 100 ++++++++++++++++ .../borrow_check/nll/region_infer/graphviz.rs | 71 +++++++++++ .../borrow_check/nll/region_infer/mod.rs | 4 + src/libsyntax/feature_gate.rs | 6 + .../mir-opt/nll/liveness-call-subtlety.rs | 8 +- .../mir-opt/nll/liveness-drop-intra-block.rs | 10 +- src/test/mir-opt/nll/liveness-interblock.rs | 8 +- src/test/mir-opt/nll/named-lifetimes-basic.rs | 17 ++- src/test/mir-opt/nll/reborrow-basic.rs | 7 +- src/test/mir-opt/nll/region-liveness-basic.rs | 14 +-- .../nll/region-liveness-drop-may-dangle.rs | 2 +- .../nll/region-liveness-drop-no-may-dangle.rs | 2 +- .../nll/region-liveness-two-disjoint-uses.rs | 6 +- .../mir-opt/nll/region-subtyping-basic.rs | 6 +- src/test/ui/nll/capture-ref-in-struct.rs | 50 ++++++++ src/test/ui/nll/capture-ref-in-struct.stderr | 13 ++ .../escape-argument-callee.rs | 48 ++++++++ .../escape-argument-callee.stderr | 40 +++++++ .../closure-requirements/escape-argument.rs | 50 ++++++++ .../escape-argument.stderr | 39 ++++++ .../escape-upvar-nested.rs | 43 +++++++ .../escape-upvar-nested.stderr | 61 ++++++++++ .../closure-requirements/escape-upvar-ref.rs | 35 ++++++ .../escape-upvar-ref.stderr | 42 +++++++ .../propagate-approximated-fail-no-postdom.rs | 63 ++++++++++ ...pagate-approximated-fail-no-postdom.stderr | 46 ++++++++ .../propagate-approximated-ref.rs | 64 ++++++++++ .../propagate-approximated-ref.stderr | 36 ++++++ .../propagate-approximated-to-empty.rs | 47 ++++++++ .../propagate-approximated-to-empty.stderr | 46 ++++++++ .../propagate-approximated-to-static.rs | 46 ++++++++ .../propagate-approximated-to-static.stderr | 36 ++++++ .../propagate-approximated-val.rs | 52 ++++++++ .../propagate-approximated-val.stderr | 36 ++++++ .../propagate-despite-same-free-region.rs | 59 ++++++++++ .../propagate-despite-same-free-region.stderr | 37 ++++++ .../region-ebr-does-not-outlive-static.rs | 26 ++++ .../region-ebr-does-not-outlive-static.stderr | 14 +++ ...region-lbr-anon-does-not-outlive-static.rs | 25 ++++ ...on-lbr-anon-does-not-outlive-static.stderr | 32 +++++ ...egion-lbr-named-does-not-outlive-static.rs | 24 ++++ ...n-lbr-named-does-not-outlive-static.stderr | 14 +++ .../region-lbr1-does-not-outlive-ebr2.rs} | 8 +- .../region-lbr1-does-not-outlive-ebr2.stderr | 14 +++ ...does-outlive-lbr2-because-implied-bound.rs | 23 ++++ .../return-wrong-bound-region.rs | 34 ++++++ .../return-wrong-bound-region.stderr | 38 ++++++ .../via-upvar-nested.stderr | 87 ++++++++++++++ src/test/ui/nll/named-region-basic.stderr | 31 ----- 51 files changed, 1702 insertions(+), 77 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/region_infer/annotation.rs create mode 100644 src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs create mode 100644 src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs create mode 100644 src/test/ui/nll/capture-ref-in-struct.rs create mode 100644 src/test/ui/nll/capture-ref-in-struct.stderr create mode 100644 src/test/ui/nll/closure-requirements/escape-argument-callee.rs create mode 100644 src/test/ui/nll/closure-requirements/escape-argument-callee.stderr create mode 100644 src/test/ui/nll/closure-requirements/escape-argument.rs create mode 100644 src/test/ui/nll/closure-requirements/escape-argument.stderr create mode 100644 src/test/ui/nll/closure-requirements/escape-upvar-nested.rs create mode 100644 src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr create mode 100644 src/test/ui/nll/closure-requirements/escape-upvar-ref.rs create mode 100644 src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-to-static.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-val.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr create mode 100644 src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr create mode 100644 src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.rs create mode 100644 src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.stderr create mode 100644 src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs create mode 100644 src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr create mode 100644 src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs create mode 100644 src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr rename src/test/ui/nll/{named-region-basic.rs => closure-requirements/region-lbr1-does-not-outlive-ebr2.rs} (81%) create mode 100644 src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr create mode 100644 src/test/ui/nll/closure-requirements/region-lbr1-does-outlive-lbr2-because-implied-bound.rs create mode 100644 src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs create mode 100644 src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr create mode 100644 src/test/ui/nll/closure-requirements/via-upvar-nested.stderr delete mode 100644 src/test/ui/nll/named-region-basic.stderr diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 3d698abc83f43..4ff299efb9555 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -14,6 +14,7 @@ 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; @@ -117,8 +119,19 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( 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, + ); + + // 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) } @@ -134,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; @@ -168,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. @@ -184,13 +206,90 @@ 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 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 171deb3e1d753..b2e2ccc5d0b72 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -25,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 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/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..dae8c84224015 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs @@ -0,0 +1,48 @@ +// 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* the signature of the closure doesn't indicate that `y` lives +// long enough for that. The closure reports the error (and 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..a0587c797df62 --- /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:31:50 + | +31 | let mut closure = expect_sig(|p, y| *p = y); + | ^ + +error: free region `'_#4r` does not outlive free region `'_#3r` + --> $DIR/escape-argument-callee.rs:31:45 + | +31 | let mut closure = expect_sig(|p, y| *p = y); + | ^^^^^^ + +note: External requirements + --> $DIR/escape-argument-callee.rs:31:38 + | +31 | 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(7666))) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7667))) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0(7668))) i32)) + ] + = note: number of external vids: 1 + +note: No external requirements + --> $DIR/escape-argument-callee.rs:25:1 + | +25 | / fn test() { +26 | | let x = 44; +27 | | let mut p = &x; +28 | | +... | +37 | | deref(p); +38 | | } + | |_^ + | + = 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..da6d60e00eadb --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument.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. + +// 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 borrow that is given as `y` and forces it to live +// too long. + +// 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..653c461246247 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -0,0 +1,39 @@ +note: External requirements + --> $DIR/escape-argument.rs:34:38 + | +34 | 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(8634))) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(8635))) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(8635))) i32)) + ] + = note: number of external vids: 1 + +note: No external requirements + --> $DIR/escape-argument.rs:28:1 + | +28 | / fn test() { +29 | | let x = 44; +30 | | let mut p = &x; +31 | | +... | +39 | | deref(p); +40 | | } + | |_^ + | + = 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:36:6 + | +33 | 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-upvar-nested.rs b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs new file mode 100644 index 0000000000000..6a94f244ea68e --- /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 via-upvar, 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..5d14501cdbba7 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs @@ -0,0 +1,35 @@ +// 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` +// - stores reference to `y` into another, longer-lived spot + +// 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..0a182fbc278e8 --- /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:26:27 + | +26 | 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:20:1 + | +20 | / fn test() { +21 | | let x = 44; +22 | | let mut p = &x; +23 | | +... | +30 | | deref(p); +31 | | } + | |_^ + | + = 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:28:6 + | +25 | let y = 22; + | - temporary value created here +... +28 | } //~ 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..c52701d0bf868 --- /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(9523))) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(9523))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9524))) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(9523))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9524))) 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..a18b618a05345 --- /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: 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..6cdbd87a833f0 --- /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(7696))) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7697))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0(7698))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1(7699))) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2(9524))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7697))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3(9525))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1(7699))) 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-to-empty.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.rs new file mode 100644 index 0000000000000..8a6384d95ca0d --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.rs @@ -0,0 +1,47 @@ +// 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 fail to approximate one of the regions and +// hence report an error while checking the closure. + +// 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-approximated-to-empty.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr new file mode 100644 index 0000000000000..8d62e49adc58c --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr @@ -0,0 +1,46 @@ +warning: not reporting region error due to -Znll + --> $DIR/propagate-approximated-to-empty.rs:41:9 + | +41 | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `'_#6r` does not outlive free region `'_#4r` + --> $DIR/propagate-approximated-to-empty.rs:41:21 + | +41 | demand_y(x, y, x.get()) + | ^ + +note: External requirements + --> $DIR/propagate-approximated-to-empty.rs:39:47 + | +39 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + | _______________________________________________^ +40 | | // Only works if 'x: 'y: +41 | | demand_y(x, y, x.get()) +42 | | //~^ WARN not reporting region error due to -Znll +43 | | //~| ERROR free region `'_#6r` does not outlive free region `'_#4r` +44 | | }); + | |_____^ + | + = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_empty[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(7695))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7696))) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0(7697))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1(9522))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2(9523))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7696))) u32>)) + ] + = note: number of external vids: 2 + +note: No external requirements + --> $DIR/propagate-approximated-to-empty.rs:38:1 + | +38 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +39 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +40 | | // Only works if 'x: 'y: +41 | | demand_y(x, y, x.get()) +... | +44 | | }); +45 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ propagate_approximated_to_empty[317d]::supply[0]) with substs [] + +error: aborting due to previous error + diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.rs new file mode 100644 index 0000000000000..5e669b3d3c1c1 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.rs @@ -0,0 +1,46 @@ +// 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 forced to approximate one end-point with +// `'static`. Note that `'static` shows up in the stderr output as `'0`. + +// 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-to-static.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr new file mode 100644 index 0000000000000..d40643305e66c --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr @@ -0,0 +1,36 @@ +warning: not reporting region error due to -Znll + --> $DIR/propagate-approximated-to-static.rs:42:9 + | +42 | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ + +note: External requirements + --> $DIR/propagate-approximated-to-static.rs:40:47 + | +40 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { + | _______________________________________________^ +41 | | // Only works if 'x: 'y: +42 | | demand_y(x, y, x.get()) +43 | | }); + | |_____^ + | + = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_static[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(7695))) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7696))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0(7697))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7696))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1(9522))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2(9523))) u32>)) + ] + = note: number of external vids: 2 + = note: where '_#1r: '_#0r + +note: No external requirements + --> $DIR/propagate-approximated-to-static.rs:39:1 + | +39 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { +40 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +41 | | // Only works if 'x: 'y: +42 | | demand_y(x, y, x.get()) +43 | | }); +44 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ propagate_approximated_to_static[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..77514543e15f6 --- /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(9519))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9520))) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(9519))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9520))) 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..87fda8c4e160d --- /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(9518))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9519))) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(9518))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9519))) 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/region-ebr-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.rs new file mode 100644 index 0000000000000..fc8b48b855134 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.rs @@ -0,0 +1,26 @@ +// 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 + +fn foo<'a>(x: &'a u32) -> &'static u32 + where 'static: 'a +{ + &*x + //~^ WARN not reporting region error due to -Znll + //~| ERROR free region `'a` does not outlive free region `'static` +} + +fn main() { } diff --git a/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.stderr new file mode 100644 index 0000000000000..d3fbbb81df928 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.stderr @@ -0,0 +1,14 @@ +warning: not reporting region error due to -Znll + --> $DIR/region-ebr-does-not-outlive-static.rs:21:5 + | +21 | &*x + | ^^^ + +error: free region `'a` does not outlive free region `'static` + --> $DIR/region-ebr-does-not-outlive-static.rs:21:5 + | +21 | &*x + | ^^^ + +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..ec661bfb8e5f5 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs @@ -0,0 +1,25 @@ +// 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 + +fn foo(x: &u32) -> &'static u32 { + &*x + //~^ WARN not reporting region error due to -Znll + //~| ERROR `*x` does not live long enough + //~| ERROR free region `'_#1r` does not outlive free region `'static` +} + +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..d5d4a5642ea8e --- /dev/null +++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr @@ -0,0 +1,32 @@ +warning: not reporting region error due to -Znll + --> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:5 + | +19 | &*x + | ^^^ + +error[E0597]: `*x` does not live long enough + --> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:6 + | +19 | &*x + | ^^ does not live long enough + | + = note: borrowed value must be valid for the static lifetime... +note: ...but borrowed value is only valid for the anonymous lifetime #1 defined on the function body at 18:1 + --> $DIR/region-lbr-anon-does-not-outlive-static.rs:18:1 + | +18 | / fn foo(x: &u32) -> &'static u32 { +19 | | &*x +20 | | //~^ WARN not reporting region error due to -Znll +21 | | //~| ERROR `*x` does not live long enough +22 | | //~| ERROR free region `'_#1r` does not outlive free region `'static` +23 | | } + | |_^ + +error: free region `'_#1r` does not outlive free region `'static` + --> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:5 + | +19 | &*x + | ^^^ + +error: aborting due to 2 previous errors + 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..f70441c08d843 --- /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 + +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 `'static` +} + +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..a54e7b612c140 --- /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 `'static` + --> $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 81% 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..94a234bc3d371 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 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..d63fd04cd2b7f --- /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 +// 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..ba0c6cd895c7a --- /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(7661))) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(8630))) i32)) -> &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(7661))) 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/closure-requirements/via-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/via-upvar-nested.stderr new file mode 100644 index 0000000000000..ba06888943b91 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/via-upvar-nested.stderr @@ -0,0 +1,87 @@ +note: External requirements + --> $DIR/via-upvar-nested.rs:24:28 + | +24 | let closure1 = || p = &y; + | ^^^^^^^^^ + | + = note: _0: () + = note: _1: &mut [closure@$DIR/via-upvar-nested.rs:24:28: 24:37 p:&mut &i32, y:&i32] + +note: External requirements + --> $DIR/via-upvar-nested.rs:23:27 + | +23 | let mut closure = || { + | ___________________________^ +24 | | let closure1 = || p = &y; +25 | | closure1(); +26 | | }; + | |_________^ + | + = note: _0: () + = note: _1: &mut [closure@$DIR/via-upvar-nested.rs:23:27: 26:10 p:&mut &i32, y:&i32] + = note: where '_#1r: '_#2r + +error[E0596]: cannot borrow immutable item `closure1` as mutable (Mir) + --> $DIR/via-upvar-nested.rs:25:13 + | +25 | closure1(); + | ^^^^^^^^ cannot borrow as mutable + +error[E0597]: `**y` does not live long enough (Ast) + --> $DIR/via-upvar-nested.rs:24:36 + | +24 | let closure1 = || p = &y; + | -- ^ does not live long enough + | | + | capture occurs here +... +29 | } + | - borrowed value only lives until here +... +32 | } + | - borrowed value needs to live until here + +error[E0596]: cannot borrow immutable local variable `closure1` as mutable (Ast) + --> $DIR/via-upvar-nested.rs:25:13 + | +24 | let closure1 = || p = &y; + | -------- consider changing this to `mut closure1` +25 | closure1(); + | ^^^^^^^^ cannot borrow mutably + +note: No external requirements + --> $DIR/via-upvar-nested.rs:16:1 + | +16 | / fn test() { +17 | | let x = 44; +18 | | let mut p = &x; +19 | | +... | +31 | | deref(p); +32 | | } + | |_^ + +error[E0597]: borrowed value does not live long enough (Mir) + --> $DIR/via-upvar-nested.rs:29:6 + | +21 | let y = 22; + | - temporary value created here +... +29 | } + | ^ temporary value dropped here while still borrowed + | + = note: consider using a `let` binding to increase its lifetime + +error[E0502]: cannot borrow `(*p)` as immutable because it is also borrowed as mutable (Mir) + --> $DIR/via-upvar-nested.rs:31:11 + | +23 | let mut closure = || { + | -- mutable borrow occurs here +24 | let closure1 = || p = &y; + | - previous borrow occurs due to use of `(*p)` in closure +... +31 | deref(p); + | ^ immutable borrow occurs here + +error: aborting due to 5 previous errors + 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 - From d5ef3e262f23d419750d7d96dce69ff17baf0e5b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 1 Dec 2017 08:51:01 -0500 Subject: [PATCH 05/21] replace `InferCtxt::fn_sig` with `closure_sig` --- src/librustc/infer/mod.rs | 44 +++++-------------- src/librustc/traits/project.rs | 17 +++---- src/librustc/traits/select.rs | 3 +- src/librustc/ty/sty.rs | 4 ++ .../borrow_check/nll/universal_regions.rs | 10 +++++ src/librustc_typeck/check/callee.rs | 3 +- src/librustc_typeck/check/coercion.rs | 3 +- 7 files changed, 38 insertions(+), 46 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 0d4d294ad36f3..f5595d07340bb 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1480,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/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/sty.rs b/src/librustc/ty/sty.rs index 24cf14419e4ae..1755382516af4 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, diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 35c50f941907a..b614e280c5525 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -381,7 +381,10 @@ 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()); @@ -393,8 +396,10 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { 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); @@ -410,12 +415,14 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { // 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); } @@ -562,6 +569,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { /// /// 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); @@ -576,6 +584,8 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { 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`. 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, _) => { From b4d71ea6f8165b326f065c5db8c7c07ad61bafd3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 1 Dec 2017 09:26:13 -0500 Subject: [PATCH 06/21] make `fn_sig().subst()` ICE when used with a closure It's inefficient, and the substitution there doesn't account for the extra regions used by NLL inference, so it's a bad thing to encourage. As it happens all callers already know if they have a closure or not, from what I can tell. --- src/librustc/ty/instance.rs | 4 ++-- src/librustc_trans/common.rs | 4 ++-- src/librustc_trans/mir/constant.rs | 5 ++--- src/librustc_trans_utils/monomorphize.rs | 4 ++-- src/librustc_typeck/collect.rs | 26 ++++++++++++++++-------- 5 files changed, 25 insertions(+), 18 deletions(-) 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_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/collect.rs b/src/librustc_typeck/collect.rs index b754c981b2101..88b9f3cfb9371 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(..), .. }) => { + // In order to property accommodate regions during NLL + // inference, `fn_sig` query only works for top-level + // functions. This is because closures often contain erased regions + // in their signatures that are understood by NLL inference but not other + // parts of the system -- these do not appear in the generics and hence + // are not properly substituted away without some care. + // + // 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 => { From 681f54f7950847e6aec4d10bd0565e30d54860dd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 17 Nov 2017 14:08:47 -0500 Subject: [PATCH 07/21] fix filter to support `&` and `|` --- src/librustc_mir/util/pretty.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 98d746952a402..37f59773cd6f0 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -57,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>, @@ -104,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) + }) }) } @@ -357,7 +369,8 @@ fn write_extra<'cx, 'gcx, 'tcx, F>( write: &mut Write, mut visit_op: F, ) -> io::Result<()> -where F: FnMut(&mut ExtraComments<'cx, 'gcx, 'tcx>) +where + F: FnMut(&mut ExtraComments<'cx, 'gcx, 'tcx>), { let mut extra_comments = ExtraComments { _tcx: tcx, From 47b8ef51d43850f6f1e371501b9add2f1631cafd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Dec 2017 17:05:06 -0500 Subject: [PATCH 08/21] don't print self symbol's internal index unless gensymed It's just not useful. It also makes it hard to have tests that probe internal state, since the interning number is very sensitive. Dumping the number in the case of gensym is not ideal but will do for now. --- src/libsyntax_pos/symbol.rs | 11 ++++++++++- .../escape-argument-callee.stderr | 2 +- .../nll/closure-requirements/escape-argument.stderr | 2 +- .../propagate-approximated-fail-no-postdom.stderr | 2 +- .../propagate-approximated-ref.stderr | 2 +- .../propagate-approximated-to-empty.stderr | 2 +- .../propagate-approximated-to-static.stderr | 2 +- .../propagate-approximated-val.stderr | 2 +- .../propagate-despite-same-free-region.stderr | 2 +- .../return-wrong-bound-region.stderr | 2 +- 10 files changed, 19 insertions(+), 10 deletions(-) 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/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index a0587c797df62..2d36143d5b324 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -18,7 +18,7 @@ note: External requirements | = 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(7666))) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7667))) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0(7668))) i32)) + 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 diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 653c461246247..aec1302082323 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -6,7 +6,7 @@ note: External requirements | = 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(8634))) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(8635))) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(8635))) i32)) + 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 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 index c52701d0bf868..d581622c4c63e 100644 --- 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 @@ -24,7 +24,7 @@ note: External requirements | = 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(9523))) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(9523))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9524))) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(9523))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9524))) u32>)) + 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 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr index 6cdbd87a833f0..7553ac5b0c305 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -16,7 +16,7 @@ note: External requirements | = 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(7696))) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7697))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0(7698))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1(7699))) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2(9524))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7697))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3(9525))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1(7699))) u32>)) + 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 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr index 8d62e49adc58c..ed8938dd6b9fc 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr @@ -24,7 +24,7 @@ note: External requirements | = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_empty[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(7695))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7696))) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0(7697))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1(9522))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2(9523))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7696))) u32>)) + 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 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr index d40643305e66c..c23d2a8eca3c9 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr @@ -16,7 +16,7 @@ note: External requirements | = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_static[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(7695))) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7696))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0(7697))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(7696))) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1(9522))) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2(9523))) u32>)) + 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 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index 77514543e15f6..ae5ad6f4b9602 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -16,7 +16,7 @@ note: External requirements | = 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(9519))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9520))) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(9519))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9520))) u32>)) + 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 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 index 87fda8c4e160d..64f3bb08c620c 100644 --- 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 @@ -16,7 +16,7 @@ note: External requirements | = 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(9518))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9519))) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(9518))) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(9519))) u32>)) + 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 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 index ba0c6cd895c7a..8999f69e8ded6 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -18,7 +18,7 @@ note: External requirements | = 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(7661))) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's(8630))) i32)) -> &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r(7661))) i32 + 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 From 05977fe751ae163625016bcf81a6c74c8b50a6de Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 15:15:27 -0500 Subject: [PATCH 09/21] rewrite comment on how to get closure sig, per arielb1's suggestion --- src/librustc_typeck/collect.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 88b9f3cfb9371..85b926a707db3 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1269,14 +1269,14 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => { - // In order to property accommodate regions during NLL - // inference, `fn_sig` query only works for top-level - // functions. This is because closures often contain erased regions - // in their signatures that are understood by NLL inference but not other - // parts of the system -- these do not appear in the generics and hence - // are not properly substituted away without some care. + // 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 + // To get + // the signature of a closure, you should use the // `closure_sig` method on the `ClosureSubsts`: // // closure_substs.closure_sig(def_id, tcx) From dac59ac93afa3ca13adc1bab5eaa9bd2445ee868 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:18:28 -0500 Subject: [PATCH 10/21] escape-argument-callee: improve comment per arielb1's suggestions --- .../escape-argument-callee.rs | 15 +++++++---- .../escape-argument-callee.stderr | 26 +++++++++---------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.rs b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs index dae8c84224015..1e34aaf1ea030 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.rs +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs @@ -10,12 +10,17 @@ // Test closure that: // -// - takes an argument `y` -// - stores `y` into another, longer-lived spot +// - takes an argument `y` with lifetime `'a` (in the code, it's anonymous) +// - stores `y` into another, longer-lived spot with lifetime `'b` // -// *but* the signature of the closure doesn't indicate that `y` lives -// long enough for that. The closure reports the error (and hence we -// see it before the closure's "external requirements" report). +// 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 diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index 2d36143d5b324..c842d51a2ad74 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -1,19 +1,19 @@ warning: not reporting region error due to -Znll - --> $DIR/escape-argument-callee.rs:31:50 + --> $DIR/escape-argument-callee.rs:36:50 | -31 | let mut closure = expect_sig(|p, y| *p = y); +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:31:45 + --> $DIR/escape-argument-callee.rs:36:45 | -31 | let mut closure = expect_sig(|p, y| *p = y); +36 | let mut closure = expect_sig(|p, y| *p = y); | ^^^^^^ note: External requirements - --> $DIR/escape-argument-callee.rs:31:38 + --> $DIR/escape-argument-callee.rs:36:38 | -31 | let mut closure = expect_sig(|p, y| *p = y); +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 [ @@ -23,15 +23,15 @@ note: External requirements = note: number of external vids: 1 note: No external requirements - --> $DIR/escape-argument-callee.rs:25:1 + --> $DIR/escape-argument-callee.rs:30:1 | -25 | / fn test() { -26 | | let x = 44; -27 | | let mut p = &x; -28 | | +30 | / fn test() { +31 | | let x = 44; +32 | | let mut p = &x; +33 | | ... | -37 | | deref(p); -38 | | } +42 | | deref(p); +43 | | } | |_^ | = note: defining type: DefId(0/0:3 ~ escape_argument_callee[317d]::test[0]) with substs [] From 782b9f5c0ab27c3b4dd2689b0cb1f534b6a1498d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:18:43 -0500 Subject: [PATCH 11/21] escape-argument: improve comment per arielb1's suggestion --- .../closure-requirements/escape-argument.rs | 6 +++-- .../escape-argument.stderr | 24 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/test/ui/nll/closure-requirements/escape-argument.rs b/src/test/ui/nll/closure-requirements/escape-argument.rs index da6d60e00eadb..1d8a916345e22 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.rs +++ b/src/test/ui/nll/closure-requirements/escape-argument.rs @@ -17,8 +17,10 @@ // enough to store `y`. // // The error is reported in the caller: invoking the closure links the -// lifetime of the borrow that is given as `y` and forces it to live -// too long. +// 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 diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index aec1302082323..e5c7139573eb9 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -1,7 +1,7 @@ note: External requirements - --> $DIR/escape-argument.rs:34:38 + --> $DIR/escape-argument.rs:36:38 | -34 | let mut closure = expect_sig(|p, y| *p = y); +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 [ @@ -11,26 +11,26 @@ note: External requirements = note: number of external vids: 1 note: No external requirements - --> $DIR/escape-argument.rs:28:1 + --> $DIR/escape-argument.rs:30:1 | -28 | / fn test() { -29 | | let x = 44; -30 | | let mut p = &x; -31 | | +30 | / fn test() { +31 | | let x = 44; +32 | | let mut p = &x; +33 | | ... | -39 | | deref(p); -40 | | } +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:36:6 + --> $DIR/escape-argument.rs:38:6 | -33 | let y = 22; +35 | let y = 22; | - temporary value created here ... -36 | } +38 | } | ^ temporary value dropped here while still borrowed | = note: consider using a `let` binding to increase its lifetime From 8b657d378ee593b424f964293708f65a7da74ca9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:19:06 -0500 Subject: [PATCH 12/21] escape-upvar-nested: patch "dangling reference" to renamed test --- src/test/ui/nll/closure-requirements/escape-upvar-nested.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs index 6a94f244ea68e..9f4585bfbab8e 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// As in via-upvar, test closure that: +// As in `escape-upvar-ref.rs`, test closure that: // // - captures a variable `y` // - stores reference to `y` into another, longer-lived spot From 6ee31e2af7b512bdcc85beabadf2e91152217040 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:19:26 -0500 Subject: [PATCH 13/21] escape-upvar-ref: patch comment per arielb1 --- .../closure-requirements/escape-upvar-ref.rs | 11 +++++++-- .../escape-upvar-ref.stderr | 24 +++++++++---------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs index 5d14501cdbba7..548a5ae59690c 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs @@ -9,8 +9,15 @@ // except according to those terms. // Test closure that: -// - captures a variable `y` -// - stores reference to `y` into another, longer-lived spot +// +// - 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 diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index 0a182fbc278e8..47ba66ade6533 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -1,7 +1,7 @@ note: External requirements - --> $DIR/escape-upvar-ref.rs:26:27 + --> $DIR/escape-upvar-ref.rs:33:27 | -26 | let mut closure = || p = &y; +33 | let mut closure = || p = &y; | ^^^^^^^^^ | = note: defining type: DefId(0/1:9 ~ escape_upvar_ref[317d]::test[0]::{{closure}}[0]) with closure substs [ @@ -14,26 +14,26 @@ note: External requirements = note: where '_#3r: '_#2r note: No external requirements - --> $DIR/escape-upvar-ref.rs:20:1 + --> $DIR/escape-upvar-ref.rs:27:1 | -20 | / fn test() { -21 | | let x = 44; -22 | | let mut p = &x; -23 | | +27 | / fn test() { +28 | | let x = 44; +29 | | let mut p = &x; +30 | | ... | -30 | | deref(p); -31 | | } +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:28:6 + --> $DIR/escape-upvar-ref.rs:35:6 | -25 | let y = 22; +32 | let y = 22; | - temporary value created here ... -28 | } //~ ERROR borrowed value does not live long enough +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 From 3c42f18c376663948e48ea7516762a0adfcd24a6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:22:05 -0500 Subject: [PATCH 14/21] delete stray file --- .../via-upvar-nested.stderr | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 src/test/ui/nll/closure-requirements/via-upvar-nested.stderr diff --git a/src/test/ui/nll/closure-requirements/via-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/via-upvar-nested.stderr deleted file mode 100644 index ba06888943b91..0000000000000 --- a/src/test/ui/nll/closure-requirements/via-upvar-nested.stderr +++ /dev/null @@ -1,87 +0,0 @@ -note: External requirements - --> $DIR/via-upvar-nested.rs:24:28 - | -24 | let closure1 = || p = &y; - | ^^^^^^^^^ - | - = note: _0: () - = note: _1: &mut [closure@$DIR/via-upvar-nested.rs:24:28: 24:37 p:&mut &i32, y:&i32] - -note: External requirements - --> $DIR/via-upvar-nested.rs:23:27 - | -23 | let mut closure = || { - | ___________________________^ -24 | | let closure1 = || p = &y; -25 | | closure1(); -26 | | }; - | |_________^ - | - = note: _0: () - = note: _1: &mut [closure@$DIR/via-upvar-nested.rs:23:27: 26:10 p:&mut &i32, y:&i32] - = note: where '_#1r: '_#2r - -error[E0596]: cannot borrow immutable item `closure1` as mutable (Mir) - --> $DIR/via-upvar-nested.rs:25:13 - | -25 | closure1(); - | ^^^^^^^^ cannot borrow as mutable - -error[E0597]: `**y` does not live long enough (Ast) - --> $DIR/via-upvar-nested.rs:24:36 - | -24 | let closure1 = || p = &y; - | -- ^ does not live long enough - | | - | capture occurs here -... -29 | } - | - borrowed value only lives until here -... -32 | } - | - borrowed value needs to live until here - -error[E0596]: cannot borrow immutable local variable `closure1` as mutable (Ast) - --> $DIR/via-upvar-nested.rs:25:13 - | -24 | let closure1 = || p = &y; - | -------- consider changing this to `mut closure1` -25 | closure1(); - | ^^^^^^^^ cannot borrow mutably - -note: No external requirements - --> $DIR/via-upvar-nested.rs:16:1 - | -16 | / fn test() { -17 | | let x = 44; -18 | | let mut p = &x; -19 | | -... | -31 | | deref(p); -32 | | } - | |_^ - -error[E0597]: borrowed value does not live long enough (Mir) - --> $DIR/via-upvar-nested.rs:29:6 - | -21 | let y = 22; - | - temporary value created here -... -29 | } - | ^ temporary value dropped here while still borrowed - | - = note: consider using a `let` binding to increase its lifetime - -error[E0502]: cannot borrow `(*p)` as immutable because it is also borrowed as mutable (Mir) - --> $DIR/via-upvar-nested.rs:31:11 - | -23 | let mut closure = || { - | -- mutable borrow occurs here -24 | let closure1 = || p = &y; - | - previous borrow occurs due to use of `(*p)` in closure -... -31 | deref(p); - | ^ immutable borrow occurs here - -error: aborting due to 5 previous errors - From ca60826110dcba2014f439605c2100ae0b095394 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:25:10 -0500 Subject: [PATCH 15/21] test approximations of the "shorter side" that resort to `'static` This includes the additional case suggested by arielb1. --- ...pproximated-shorter-to-static-no-bound.rs} | 9 +++- ...ximated-shorter-to-static-no-bound.stderr} | 32 +++++------ ...roximated-shorter-to-static-wrong-bound.rs | 54 +++++++++++++++++++ ...mated-shorter-to-static-wrong-bound.stderr | 36 +++++++++++++ 4 files changed, 113 insertions(+), 18 deletions(-) rename src/test/ui/nll/closure-requirements/{propagate-approximated-to-static.rs => propagate-approximated-shorter-to-static-no-bound.rs} (76%) rename src/test/ui/nll/closure-requirements/{propagate-approximated-to-static.stderr => propagate-approximated-shorter-to-static-no-bound.stderr} (55%) create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs similarity index 76% rename from src/test/ui/nll/closure-requirements/propagate-approximated-to-static.rs rename to src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs index 5e669b3d3c1c1..f776ddc8b153f 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs @@ -8,8 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test a case where we are forced to approximate one end-point with -// `'static`. Note that `'static` shows up in the stderr output as `'0`. +// 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 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr similarity index 55% rename from src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr rename to src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index c23d2a8eca3c9..8d1b9a94ea6a1 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-to-static.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -1,20 +1,20 @@ warning: not reporting region error due to -Znll - --> $DIR/propagate-approximated-to-static.rs:42:9 + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:47:9 | -42 | demand_y(x, y, x.get()) +47 | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ note: External requirements - --> $DIR/propagate-approximated-to-static.rs:40:47 + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:47 | -40 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { +45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | _______________________________________________^ -41 | | // Only works if 'x: 'y: -42 | | demand_y(x, y, x.get()) -43 | | }); +46 | | // Only works if 'x: 'y: +47 | | demand_y(x, y, x.get()) +48 | | }); | |_____^ | - = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_static[317d]::supply[0]::{{closure}}[0]) with closure substs [ + = 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>)) ] @@ -22,15 +22,15 @@ note: External requirements = note: where '_#1r: '_#0r note: No external requirements - --> $DIR/propagate-approximated-to-static.rs:39:1 + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:44:1 | -39 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { -40 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { -41 | | // Only works if 'x: 'y: -42 | | demand_y(x, y, x.get()) -43 | | }); -44 | | } +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_to_static[317d]::supply[0]) with substs [] + = 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 [] + From 2a17b9390b60d5fa538de756d2dd14cc8ced785d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:25:49 -0500 Subject: [PATCH 16/21] test more failure cases for approximating the "longer" side as suggested by arielb1 --- .../propagate-approximated-to-empty.stderr | 46 --------------- ...e-fail-to-approximate-longer-no-bounds.rs} | 10 +++- ...ail-to-approximate-longer-no-bounds.stderr | 46 +++++++++++++++ ...fail-to-approximate-longer-wrong-bounds.rs | 57 +++++++++++++++++++ ...-to-approximate-longer-wrong-bounds.stderr | 46 +++++++++++++++ 5 files changed, 157 insertions(+), 48 deletions(-) delete mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr rename src/test/ui/nll/closure-requirements/{propagate-approximated-to-empty.rs => propagate-fail-to-approximate-longer-no-bounds.rs} (76%) create mode 100644 src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr create mode 100644 src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr deleted file mode 100644 index ed8938dd6b9fc..0000000000000 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr +++ /dev/null @@ -1,46 +0,0 @@ -warning: not reporting region error due to -Znll - --> $DIR/propagate-approximated-to-empty.rs:41:9 - | -41 | demand_y(x, y, x.get()) - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error: free region `'_#6r` does not outlive free region `'_#4r` - --> $DIR/propagate-approximated-to-empty.rs:41:21 - | -41 | demand_y(x, y, x.get()) - | ^ - -note: External requirements - --> $DIR/propagate-approximated-to-empty.rs:39:47 - | -39 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { - | _______________________________________________^ -40 | | // Only works if 'x: 'y: -41 | | demand_y(x, y, x.get()) -42 | | //~^ WARN not reporting region error due to -Znll -43 | | //~| ERROR free region `'_#6r` does not outlive free region `'_#4r` -44 | | }); - | |_____^ - | - = note: defining type: DefId(0/1:18 ~ propagate_approximated_to_empty[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-approximated-to-empty.rs:38:1 - | -38 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { -39 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { -40 | | // Only works if 'x: 'y: -41 | | demand_y(x, y, x.get()) -... | -44 | | }); -45 | | } - | |_^ - | - = note: defining type: DefId(0/0:6 ~ propagate_approximated_to_empty[317d]::supply[0]) with substs [] - -error: aborting due to previous error - diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs similarity index 76% rename from src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.rs rename to src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs index 8a6384d95ca0d..4bbdcc4494486 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.rs +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs @@ -8,8 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test a case where we fail to approximate one of the regions and -// hence report an error while checking the closure. +// 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 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 + From b8e9eaf3aeabecb3c20f20c465697d674d592e9e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:26:43 -0500 Subject: [PATCH 17/21] propagate-approximated-ref: include FIXME comment --- .../ui/nll/closure-requirements/propagate-approximated-ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs index a18b618a05345..76a0762461a2b 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs @@ -22,7 +22,7 @@ // Note: the use of `Cell` here is to introduce invariance. One less // variable. // -// FIXME: The `supply` function *ought* to generate an error, but it +// 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. From 75cf482651e004e131bb7f2580bc609587983616 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:32:04 -0500 Subject: [PATCH 18/21] region-ebr-does-not-outlive-static: reuse old test instead --- src/test/compile-fail/regions-static-bound.rs | 16 +++++++++--- .../region-ebr-does-not-outlive-static.rs | 26 ------------------- .../region-ebr-does-not-outlive-static.stderr | 14 ---------- 3 files changed, 13 insertions(+), 43 deletions(-) delete mode 100644 src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.rs delete mode 100644 src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.stderr 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/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.rs deleted file mode 100644 index fc8b48b855134..0000000000000 --- a/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.rs +++ /dev/null @@ -1,26 +0,0 @@ -// 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 - -fn foo<'a>(x: &'a u32) -> &'static u32 - where 'static: 'a -{ - &*x - //~^ WARN not reporting region error due to -Znll - //~| ERROR free region `'a` does not outlive free region `'static` -} - -fn main() { } diff --git a/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.stderr deleted file mode 100644 index d3fbbb81df928..0000000000000 --- a/src/test/ui/nll/closure-requirements/region-ebr-does-not-outlive-static.stderr +++ /dev/null @@ -1,14 +0,0 @@ -warning: not reporting region error due to -Znll - --> $DIR/region-ebr-does-not-outlive-static.rs:21:5 - | -21 | &*x - | ^^^ - -error: free region `'a` does not outlive free region `'static` - --> $DIR/region-ebr-does-not-outlive-static.rs:21:5 - | -21 | &*x - | ^^^ - -error: aborting due to previous error - From 86e7b5c12acc112fcb7b53d431b1564215de4c80 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 16:35:36 -0500 Subject: [PATCH 19/21] use `-Znll -Zborrowck=mir -Zverbose` consistently --- ...region-lbr-anon-does-not-outlive-static.rs | 5 ++--- ...on-lbr-anon-does-not-outlive-static.stderr | 22 ++----------------- ...egion-lbr-named-does-not-outlive-static.rs | 4 ++-- ...n-lbr-named-does-not-outlive-static.stderr | 2 +- .../region-lbr1-does-not-outlive-ebr2.rs | 2 +- ...does-outlive-lbr2-because-implied-bound.rs | 2 +- 6 files changed, 9 insertions(+), 28 deletions(-) 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 index ec661bfb8e5f5..c61cf8a940f95 100644 --- 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 @@ -13,13 +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(x: &u32) -> &'static u32 { &*x //~^ WARN not reporting region error due to -Znll - //~| ERROR `*x` does not live long enough - //~| ERROR free region `'_#1r` does not outlive free region `'static` + //~| 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 index d5d4a5642ea8e..ef7ea9239127b 100644 --- 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 @@ -4,29 +4,11 @@ warning: not reporting region error due to -Znll 19 | &*x | ^^^ -error[E0597]: `*x` does not live long enough - --> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:6 - | -19 | &*x - | ^^ does not live long enough - | - = note: borrowed value must be valid for the static lifetime... -note: ...but borrowed value is only valid for the anonymous lifetime #1 defined on the function body at 18:1 - --> $DIR/region-lbr-anon-does-not-outlive-static.rs:18:1 - | -18 | / fn foo(x: &u32) -> &'static u32 { -19 | | &*x -20 | | //~^ WARN not reporting region error due to -Znll -21 | | //~| ERROR `*x` does not live long enough -22 | | //~| ERROR free region `'_#1r` does not outlive free region `'static` -23 | | } - | |_^ - -error: free region `'_#1r` does not outlive free region `'static` +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 2 previous errors +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 index f70441c08d843..fcda5c5420bfe 100644 --- 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 @@ -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 -Zborrowck=mir +// 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 `'static` + //~| 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 index a54e7b612c140..6dcb8e7cf122d 100644 --- 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 @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 19 | &*x | ^^^ -error: free region `'_#1r` does not outlive free region `'static` +error: free region `'_#1r` does not outlive free region `ReStatic` --> $DIR/region-lbr-named-does-not-outlive-static.rs:19:5 | 19 | &*x diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs index 94a234bc3d371..c1e4dee00656b 100644 --- a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs @@ -13,7 +13,7 @@ // 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 +// compile-flags:-Znll -Zborrowck=mir -Zverbose fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 { &*x 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 index d63fd04cd2b7f..ffb1935e75e32 100644 --- 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 @@ -11,7 +11,7 @@ // 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 +// compile-flags:-Znll -Zborrowck=mir -Zverbose // must-compile-successfully #![allow(warnings)] From 47037705dc16b888184751dd086046adcddeaeea Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Dec 2017 05:20:12 -0500 Subject: [PATCH 20/21] add test comparing free region to bound region suggested by arielb1 --- ...horter-to-static-comparing-against-free.rs | 53 +++++++++++++ ...er-to-static-comparing-against-free.stderr | 75 +++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs create mode 100644 src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr 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 + From 1db58d7fa5b5665d12b25ac3695bb3fa9606276e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Dec 2017 05:24:59 -0500 Subject: [PATCH 21/21] rename `Binder::new_not_binding` to `Binder::dummy` per pnkfelix's suggestion --- src/librustc/ty/sty.rs | 2 +- src/librustc_mir/borrow_check/nll/universal_regions.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 1755382516af4..4f733b8f68a36 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -654,7 +654,7 @@ impl Binder { /// 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 new_not_binding<'tcx>(value: T) -> Binder + pub fn dummy<'tcx>(value: T) -> Binder where T: TypeFoldable<'tcx> { assert!(!value.has_escaping_regions()); diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index b614e280c5525..857a620cead65 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -545,7 +545,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { 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::new_not_binding(inputs_and_output) + ty::Binder::dummy(inputs_and_output) } ty::TyFnDef(def_id, _) => {