diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 32577ac46f3b1..df67c3abbe86c 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -536,14 +536,29 @@ impl<'gcx> HashStable> for mir::Literal<'gcx> { impl_stable_hash_for!(struct mir::Location { block, statement_index }); -impl_stable_hash_for!(struct mir::ClosureRegionRequirements { +impl_stable_hash_for!(struct mir::ClosureRegionRequirements<'tcx> { num_external_vids, outlives_requirements }); -impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement { - free_region, +impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> { + subject, outlived_free_region, blame_span }); +impl<'gcx> HashStable> for mir::ClosureOutlivesSubject<'gcx> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + match *self { + mir::ClosureOutlivesSubject::Ty(ref ty) => { + ty.hash_stable(hcx, hasher); + } + mir::ClosureOutlivesSubject::Region(ref region) => { + region.hash_stable(hcx, hasher); + } + } + } +} diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 2655e2acbbdfb..ea3a1074aa269 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -75,6 +75,9 @@ for ty::RegionKind { ty::ReFree(ref free_region) => { free_region.hash_stable(hcx, hasher); } + ty::ReClosureBound(vid) => { + vid.hash_stable(hcx, hasher); + } ty::ReLateBound(..) | ty::ReVar(..) | ty::ReSkolemized(..) => { diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 50a37e12531a7..f7bc092a3d7ae 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -475,6 +475,14 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, ' ty::Bivariant | ty::Covariant | ty::Contravariant => (), } } + + ty::ReClosureBound(..) => { + span_bug!( + self.span, + "encountered unexpected ReClosureBound: {:?}", + r, + ); + } } // FIXME: This is non-ideal because we don't give a diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 514b29120a96a..3e3aea0256e72 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -240,6 +240,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::ReErased => { (format!("lifetime {:?}", region), None) } + + // We shouldn't encounter an error message with ReClosureBound. + ty::ReClosureBound(..) => { + bug!( + "encountered unexpected ReClosureBound: {:?}", + region, + ); + } }; let message = format!("{}{}{}", prefix, description, suffix); if let Some(span) = span { diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 426c61e9ac083..1783d5abfc7c6 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -113,6 +113,13 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { // replace all free regions with 'erased self.tcx().types.re_erased } + + ty::ReClosureBound(..) => { + bug!( + "encountered unexpected ReClosureBound: {:?}", + r, + ); + } } } diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 5a4f2157298b0..3ac4ec5bee416 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -258,7 +258,12 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let tcx = self.region_rels.tcx; match (a, b) { - (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { + (&ty::ReClosureBound(..), _) | + (_, &ty::ReClosureBound(..)) | + (&ReLateBound(..), _) | + (_, &ReLateBound(..)) | + (&ReErased, _) | + (_, &ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index a83c3f29d25bc..dd3dd1e06de37 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1831,8 +1831,17 @@ pub struct GeneratorLayout<'tcx> { /// instance of the closure is created, the corresponding free regions /// can be extracted from its type and constrained to have the given /// outlives relationship. +/// +/// In some cases, we have to record outlives requirements between +/// types and regions as well. In that case, if those types include +/// any regions, those regions are recorded as `ReClosureBound` +/// instances assigned one of these same indices. Those regions will +/// be substituted away by the creator. We use `ReClosureBound` in +/// that case because the regions must be allocated in the global +/// TyCtxt, and hence we cannot use `ReVar` (which is what we use +/// internally within the rest of the NLL code). #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] -pub struct ClosureRegionRequirements { +pub struct ClosureRegionRequirements<'gcx> { /// 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 @@ -1842,15 +1851,15 @@ pub struct ClosureRegionRequirements { /// Requirements between the various free regions defined in /// indices. - pub outlives_requirements: Vec, + 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, RustcEncodable, RustcDecodable)] -pub struct ClosureOutlivesRequirement { - // This region ... - pub free_region: ty::RegionVid, +/// Indicates an outlives constraint between a type or between two +/// free-regions declared on the closure. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct ClosureOutlivesRequirement<'tcx> { + // This region or type ... + pub subject: ClosureOutlivesSubject<'tcx>, // .. must outlive this one. pub outlived_free_region: ty::RegionVid, @@ -1859,6 +1868,23 @@ pub struct ClosureOutlivesRequirement { pub blame_span: Span, } +/// The subject of a ClosureOutlivesRequirement -- that is, the thing +/// that must outlive some region. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum ClosureOutlivesSubject<'tcx> { + /// Subject is a type, typically a type parameter, but could also + /// be a projection. Indicates a requirement like `T: 'a` being + /// passed to the caller, where the type here is `T`. + /// + /// The type here is guaranteed not to contain any free regions at + /// present. + Ty(Ty<'tcx>), + + /// Subject is a free region from the closure. Indicates a requirement + /// like `'a: 'b` being passed to the caller; the region here is `'a`. + Region(ty::RegionVid), +} + /* * TypeFoldable implementations for MIR types */ diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 069dc0275cbb1..c5b82730e488c 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -97,14 +97,19 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn has_closure_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_CLOSURE) } + /// "Free" regions in this context means that it has any region + /// that is not (a) erased or (b) late-bound. + fn has_free_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) + } + + /// True if there any any un-erased free regions. fn has_erasable_regions(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND | - TypeFlags::HAS_RE_INFER | - TypeFlags::HAS_FREE_REGIONS) + self.has_type_flags(TypeFlags::HAS_FREE_REGIONS) } + fn is_normalized_for_trans(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND | - TypeFlags::HAS_RE_INFER | + !self.has_type_flags(TypeFlags::HAS_RE_INFER | TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_TY_INFER | TypeFlags::HAS_PARAMS | diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 7ba063adff4c2..319f63dd7c84a 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -193,7 +193,7 @@ define_maps! { <'tcx> /// 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, + [] 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/mod.rs b/src/librustc/ty/mod.rs index 4e1a79d4613e7..9d088b95bbbb5 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -421,8 +421,18 @@ bitflags! { const HAS_TY_INFER = 1 << 2; const HAS_RE_INFER = 1 << 3; const HAS_RE_SKOL = 1 << 4; + + /// Does this have any `ReEarlyBound` regions? Used to + /// determine whether substitition is required, since those + /// represent regions that are bound in a `ty::Generics` and + /// hence may be substituted. const HAS_RE_EARLY_BOUND = 1 << 5; + + /// Does this have any region that "appears free" in the type? + /// Basically anything but `ReLateBound` and `ReErased`. const HAS_FREE_REGIONS = 1 << 6; + + /// Is an error type reachable? const HAS_TY_ERR = 1 << 7; const HAS_PROJECTION = 1 << 8; diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index e085d1311c3ff..02729c6d60084 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1036,6 +1036,12 @@ pub enum RegionKind { /// Erased region, used by trait selection, in MIR and during trans. ReErased, + + /// These are regions bound in the "defining type" for a + /// closure. They are used ONLY as part of the + /// `ClosureRegionRequirements` that are produced by MIR borrowck. + /// See `ClosureRegionRequirements` for more details. + ReClosureBound(RegionVid), } impl<'tcx> serialize::UseSpecializedDecodable for Region<'tcx> {} @@ -1184,18 +1190,32 @@ impl RegionKind { match *self { ty::ReVar(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX; } ty::ReSkolemized(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; flags = flags | TypeFlags::HAS_RE_SKOL; flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX; } ty::ReLateBound(..) => { } - ty::ReEarlyBound(..) => { flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; } - ty::ReStatic | ty::ReErased => { } - _ => { flags = flags | TypeFlags::HAS_FREE_REGIONS; } + ty::ReEarlyBound(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; + } + ty::ReEmpty | + ty::ReStatic | + ty::ReFree { .. } | + ty::ReScope { .. } => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + } + ty::ReErased => { + } + ty::ReClosureBound(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + } } match *self { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 2e9e45c9ffe16..84d5f547f1b75 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -822,6 +822,8 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => { self.def_id(def_id); } + + ty::ReClosureBound(..) | ty::ReLateBound(..) | ty::ReFree(..) | ty::ReScope(..) | diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 9ff3d73f5c40e..5bfa646456857 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -733,6 +733,9 @@ define_print! { ty::ReErased => Ok(()), ty::ReStatic => write!(f, "'static"), ty::ReEmpty => write!(f, "'"), + + // The user should never encounter these in unsubstituted form. + ty::ReClosureBound(vid) => write!(f, "{:?}", vid), } } debug { @@ -743,6 +746,11 @@ define_print! { data.name) } + ty::ReClosureBound(ref vid) => { + write!(f, "ReClosureBound({:?})", + vid) + } + ty::ReLateBound(binder_id, ref bound_region) => { write!(f, "ReLateBound({:?}, {:?})", binder_id, diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index 8654f2a50e46b..5cbe2822e5c03 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -367,6 +367,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { ty::ReStatic => self.item_ub, ty::ReEmpty | + ty::ReClosureBound(..) | ty::ReLateBound(..) | ty::ReVar(..) | ty::ReSkolemized(..) | diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 31a94499fd0cc..2c0fa9878aaf2 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -381,6 +381,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { }, (RegionKind::ReLateBound(_, _), _) | (RegionKind::ReSkolemized(_, _), _) | + (RegionKind::ReClosureBound(_), _) | (RegionKind::ReErased, _) => { span_bug!(drop_span, "region does not make sense in this context"); }, diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 39bcd2b6ae063..e0b03aec69a3b 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -22,16 +22,16 @@ use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; use rustc::mir::ClosureRegionRequirements; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::indexed_set::{IdxSetBuf}; +use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; use syntax::ast; use syntax_pos::Span; use dataflow::{do_dataflow, DebugFormatted}; +use dataflow::FlowAtLocation; use dataflow::MoveDataParamEnv; use dataflow::{DataflowAnalysis, DataflowResultsConsumer}; -use dataflow::{FlowAtLocation, FlowsAtLocation}; use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{EverInitializedLvals, MovingOutStatements}; use dataflow::{Borrows, BorrowData, ReserveOrActivateIndex}; @@ -65,7 +65,7 @@ pub fn provide(providers: &mut Providers) { fn mir_borrowck<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, -) -> Option { +) -> Option> { let input_mir = tcx.mir_validated(def_id); debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); @@ -89,7 +89,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, input_mir: &Mir<'gcx>, def_id: DefId, -) -> Option { +) -> Option> { let tcx = infcx.tcx; let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 59e862a56af02..673e85e6b61cd 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -12,21 +12,13 @@ use rustc::hir; use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue}; use rustc::mir::visit::Visitor; use rustc::mir::Place::Projection; -use rustc::mir::{Local, PlaceProjection, ProjectionElem}; +use rustc::mir::{PlaceProjection, ProjectionElem}; use rustc::mir::visit::TyContext; use rustc::infer::InferCtxt; -use rustc::traits::{self, ObligationCause}; -use rustc::ty::{self, ClosureSubsts, Ty}; +use rustc::ty::{self, ClosureSubsts}; use rustc::ty::subst::Substs; use rustc::ty::fold::TypeFoldable; -use rustc::util::common::ErrorReported; -use rustc_data_structures::fx::FxHashSet; -use syntax::codemap::DUMMY_SP; -use borrow_check::{FlowAtLocation, FlowsAtLocation}; -use dataflow::MaybeInitializedLvals; -use dataflow::move_paths::{HasMoveData, MoveData}; -use super::LivenessResults; use super::ToRegionVid; use super::region_infer::RegionInferenceContext; @@ -34,19 +26,11 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( infcx: &InferCtxt<'cx, 'gcx, 'tcx>, regioncx: &mut RegionInferenceContext<'tcx>, mir: &Mir<'tcx>, - param_env: ty::ParamEnv<'tcx>, - liveness: &LivenessResults, - flow_inits: &mut FlowAtLocation>, - move_data: &MoveData<'tcx>, ) { let mut cg = ConstraintGeneration { infcx, regioncx, mir, - liveness, - param_env, - flow_inits, - move_data, }; for (bb, data) in mir.basic_blocks().iter_enumerated() { @@ -59,16 +43,10 @@ struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> { infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>, regioncx: &'cg mut RegionInferenceContext<'tcx>, mir: &'cg Mir<'tcx>, - liveness: &'cg LivenessResults, - param_env: ty::ParamEnv<'tcx>, - flow_inits: &'cg mut FlowAtLocation>, - move_data: &'cg MoveData<'tcx>, } - impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> { fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) { - self.add_liveness_constraints(bb); self.super_basic_block_data(bb, data); } @@ -130,84 +108,6 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx } impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { - /// Liveness constraints: - /// - /// > If a variable V is live at point P, then all regions R in the type of V - /// > must include the point P. - fn add_liveness_constraints(&mut self, bb: BasicBlock) { - debug!("add_liveness_constraints(bb={:?})", bb); - - self.liveness - .regular - .simulate_block(self.mir, bb, |location, live_locals| { - for live_local in live_locals.iter() { - let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_regular_live_constraint(live_local_ty, location); - } - }); - - let mut all_live_locals: Vec<(Location, Vec)> = vec![]; - self.liveness - .drop - .simulate_block(self.mir, bb, |location, live_locals| { - all_live_locals.push((location, live_locals.iter().collect())); - }); - debug!( - "add_liveness_constraints: all_live_locals={:#?}", - all_live_locals - ); - - let terminator_index = self.mir.basic_blocks()[bb].statements.len(); - self.flow_inits.reset_to_entry_of(bb); - while let Some((location, live_locals)) = all_live_locals.pop() { - for live_local in live_locals { - debug!( - "add_liveness_constraints: location={:?} live_local={:?}", - location, - live_local - ); - - self.flow_inits.each_state_bit(|mpi_init| { - debug!( - "add_liveness_constraints: location={:?} initialized={:?}", - location, - &self.flow_inits - .operator() - .move_data() - .move_paths[mpi_init] - ); - }); - - let mpi = self.move_data.rev_lookup.find_local(live_local); - if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { - debug!( - "add_liveness_constraints: mpi={:?} has initialized child {:?}", - self.move_data.move_paths[mpi], - self.move_data.move_paths[initialized_child] - ); - - let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_drop_live_constraint(live_local_ty, location); - } - } - - if location.statement_index == terminator_index { - debug!( - "add_liveness_constraints: reconstruct_terminator_effect from {:#?}", - location - ); - self.flow_inits.reconstruct_terminator_effect(location); - } else { - debug!( - "add_liveness_constraints: reconstruct_statement_effect from {:#?}", - location - ); - self.flow_inits.reconstruct_statement_effect(location); - } - self.flow_inits.apply_local_effect(location); - } - } - /// Some variable with type `live_ty` is "regular live" at /// `location` -- i.e., it may be used later. This means that all /// regions appearing in the type `live_ty` must be live at @@ -230,75 +130,6 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { }); } - /// Some variable with type `live_ty` is "drop live" at `location` - /// -- i.e., it may be dropped later. This means that *some* of - /// the regions in its type must be live at `location`. The - /// precise set will depend on the dropck constraints, and in - /// particular this takes `#[may_dangle]` into account. - fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { - debug!( - "add_drop_live_constraint(dropped_ty={:?}, location={:?})", - dropped_ty, - location - ); - - let tcx = self.infcx.tcx; - let mut types = vec![(dropped_ty, 0)]; - let mut known = FxHashSet(); - while let Some((ty, depth)) = types.pop() { - let span = DUMMY_SP; // FIXME - let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { - Ok(result) => result, - Err(ErrorReported) => { - continue; - } - }; - - let ty::DtorckConstraint { - outlives, - dtorck_types, - } = result; - - // All things in the `outlives` array may be touched by - // the destructor and must be live at this point. - for outlive in outlives { - self.add_regular_live_constraint(outlive, location); - } - - // However, there may also be some types that - // `dtorck_constraint_for_ty` could not resolve (e.g., - // associated types and parameters). We need to normalize - // associated types here and possibly recursively process. - for ty in dtorck_types { - let cause = ObligationCause::dummy(); - // We know that our original `dropped_ty` is well-formed, - // so region obligations resulting from this normalization - // should always hold. - // - // Therefore we ignore them instead of trying to match - // them up with a location. - let fulfillcx = traits::FulfillmentContext::new_ignoring_regions(); - match traits::fully_normalize_with_fulfillcx( - self.infcx, fulfillcx, cause, self.param_env, &ty - ) { - Ok(ty) => match ty.sty { - ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { - self.add_regular_live_constraint(ty, location); - } - - _ => if known.insert(ty) { - types.push((ty, depth + 1)); - }, - }, - - Err(errors) => { - self.infcx.report_fulfillment_errors(&errors, None); - } - } - } - } - } - fn add_reborrow_constraint( &mut self, location: Location, diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 218142f7e2d42..6977d91d25a5d 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -9,16 +9,16 @@ // except according to those terms. use rustc::hir::def_id::DefId; -use rustc::mir::{ClosureRegionRequirements, Mir}; +use rustc::mir::{ClosureRegionRequirements, ClosureOutlivesSubject, Mir}; use rustc::infer::InferCtxt; use rustc::ty::{self, RegionKind, RegionVid}; use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; +use std::fmt::Debug; use std::io; use transform::MirSource; -use transform::type_check; -use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; -use borrow_check::FlowAtLocation; +use util::liveness::{LivenessResults, LocalSet}; +use dataflow::FlowAtLocation; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::MoveData; @@ -27,14 +27,15 @@ use util::pretty::{self, ALIGN}; use self::mir_util::PassWhere; mod constraint_generation; +pub(crate) mod region_infer; +mod renumber; mod subtype_constraint_generation; +pub(crate) mod type_check; mod universal_regions; -use self::universal_regions::UniversalRegions; -pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; +use self::universal_regions::UniversalRegions; -mod renumber; /// Rewrites the regions in the MIR to use NLL variables, also /// scraping out the set of universal regions (e.g., region parameters) @@ -52,7 +53,7 @@ pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( let universal_regions = UniversalRegions::new(infcx, def_id, param_env); // Replace all remaining regions with fresh inference variables. - renumber::renumber_mir(infcx, &universal_regions, mir); + renumber::renumber_mir(infcx, mir); let source = MirSource::item(def_id); mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(())); @@ -73,11 +74,24 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( move_data: &MoveData<'tcx>, ) -> ( RegionInferenceContext<'tcx>, - Option, + 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); + let liveness = &LivenessResults::compute(mir); + let fr_fn_body = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); + let constraint_sets = &type_check::type_check( + infcx, + mir_node_id, + param_env, + mir, + fr_fn_body, + universal_regions.input_tys, + universal_regions.output_ty, + &liveness, + flow_inits, + move_data, + ); // Create the region inference context, taking ownership of the region inference // data that was contained in `infcx`. @@ -85,35 +99,9 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( 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 { - regular: liveness::liveness_of_locals( - &mir, - LivenessMode { - include_regular_use: true, - include_drops: false, - }, - ), - - drop: liveness::liveness_of_locals( - &mir, - LivenessMode { - include_regular_use: false, - include_drops: true, - }, - ), - }; // Generate non-subtyping constraints. - constraint_generation::generate_constraints( - infcx, - &mut regioncx, - &mir, - param_env, - liveness, - flow_inits, - move_data, - ); + constraint_generation::generate_constraints(infcx, &mut regioncx, &mir); // Solve the region constraints. let closure_region_requirements = regioncx.solve(infcx, &mir, def_id); @@ -136,11 +124,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( (regioncx, closure_region_requirements) } -struct LivenessResults { - regular: LivenessResult, - drop: LivenessResult, -} - fn dump_mir_results<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, liveness: &LivenessResults, @@ -283,9 +266,13 @@ fn for_each_region_constraint( with_msg: &mut FnMut(&str) -> io::Result<()>, ) -> io::Result<()> { for req in &closure_region_requirements.outlives_requirements { + let subject: &Debug = match &req.subject { + ClosureOutlivesSubject::Region(subject) => subject, + ClosureOutlivesSubject::Ty(ty) => ty, + }; with_msg(&format!( "where {:?}: {:?}", - req.free_region, + subject, req.outlived_free_region, ))?; } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs new file mode 100644 index 0000000000000..59860d61ab985 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -0,0 +1,234 @@ +// 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. + +//! Module defining the `dfs` method on `RegionInferenceContext`, along with +//! its associated helper traits. + +use borrow_check::nll::universal_regions::UniversalRegions; +use borrow_check::nll::region_infer::RegionInferenceContext; +use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements, + RegionValues}; +use rustc::mir::{Location, Mir}; +use rustc::ty::RegionVid; +use rustc_data_structures::fx::FxHashSet; + +impl<'tcx> RegionInferenceContext<'tcx> { + /// Function used to satisfy or test a `R1: R2 @ P` + /// constraint. The core idea is that it performs a DFS starting + /// from `P`. The precise actions *during* that DFS depend on the + /// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more + /// details. + /// + /// Returns: + /// + /// - `Ok(true)` if the walk was completed and something changed + /// along the way; + /// - `Ok(false)` if the walk was completed with no changes; + /// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the + /// value that `op` returned. + pub(super) fn dfs(&self, mir: &Mir<'tcx>, mut op: C) -> Result + where + C: DfsOp, + { + let mut changed = false; + + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(op.start_point()); + while let Some(p) = stack.pop() { + let point_index = self.elements.index(p); + + if !op.source_region_contains(point_index) { + debug!(" not in from-region"); + continue; + } + + if !visited.insert(p) { + debug!(" already visited"); + continue; + } + + let new = op.add_to_target_region(point_index)?; + changed |= new; + + let block_data = &mir[p.block]; + + let start_stack_len = stack.len(); + + if p.statement_index < block_data.statements.len() { + stack.push(Location { + statement_index: p.statement_index + 1, + ..p + }); + } else { + stack.extend(block_data.terminator().successors().iter().map( + |&basic_block| { + Location { + statement_index: 0, + block: basic_block, + } + }, + )); + } + + if stack.len() == start_stack_len { + // If we reach the END point in the graph, then copy + // over any skolemized end points in the `from_region` + // and make sure they are included in the `to_region`. + changed |= op.add_universal_regions_outlived_by_source_to_target()?; + } + } + + Ok(changed) + } +} + +/// Customizes the operation of the `dfs` function. This function is +/// used during inference to satisfy a `R1: R2 @ P` constraint. +pub(super) trait DfsOp { + /// If this op stops the walk early, what type does it propagate? + type Early; + + /// Returns the point from which to start the DFS. + fn start_point(&self) -> Location; + + /// Returns true if the source region contains the given point. + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool; + + /// Adds the given point to the target region, returning true if + /// something has changed. Returns `Err` if we should abort the + /// walk early. + fn add_to_target_region( + &mut self, + point_index: RegionElementIndex, + ) -> Result; + + /// Adds all universal regions in the source region to the target region, returning + /// true if something has changed. + fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result; +} + +/// Used during inference to enforce a `R1: R2 @ P` constraint. For +/// each point Q we reach along the DFS, we check if Q is in R2 (the +/// "source region"). If not, we stop the walk. Otherwise, we add Q to +/// R1 (the "target region") and continue to Q's successors. If we +/// reach the end of the graph, then we add any universal regions from +/// R2 into R1. +pub(super) struct CopyFromSourceToTarget<'v> { + pub source_region: RegionVid, + pub target_region: RegionVid, + pub inferred_values: &'v mut RegionValues, + pub constraint_point: Location, +} + +impl<'v> DfsOp for CopyFromSourceToTarget<'v> { + /// We never stop the walk early. + type Early = !; + + fn start_point(&self) -> Location { + self.constraint_point + } + + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { + self.inferred_values + .contains(self.source_region, point_index) + } + + fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result { + Ok(self.inferred_values.add(self.target_region, point_index)) + } + + fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result { + Ok( + self.inferred_values + .add_universal_regions_outlived_by(self.source_region, self.target_region), + ) + } +} + +/// Used after inference to *test* a `R1: R2 @ P` constraint. For +/// each point Q we reach along the DFS, we check if Q in R2 is also +/// contained in R1. If not, we abort the walk early with an `Err` +/// condition. Similarly, if we reach the end of the graph and find +/// that R1 contains some universal region that R2 does not contain, +/// we abort the walk early. +pub(super) struct TestTargetOutlivesSource<'v, 'tcx: 'v> { + pub source_region: RegionVid, + pub target_region: RegionVid, + pub elements: &'v RegionValueElements, + pub universal_regions: &'v UniversalRegions<'tcx>, + pub inferred_values: &'v RegionValues, + pub constraint_point: Location, +} + +impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> { + /// The element that was not found within R2. + type Early = RegionElementIndex; + + fn start_point(&self) -> Location { + self.constraint_point + } + + fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool { + self.inferred_values + .contains(self.source_region, point_index) + } + + fn add_to_target_region( + &mut self, + point_index: RegionElementIndex, + ) -> Result { + if !self.inferred_values + .contains(self.target_region, point_index) + { + return Err(point_index); + } + + Ok(false) + } + + fn add_universal_regions_outlived_by_source_to_target( + &mut self, + ) -> Result { + // For all `ur_in_source` in `source_region`. + for ur_in_source in self.inferred_values + .universal_regions_outlived_by(self.source_region) + { + // Check that `target_region` outlives `ur_in_source`. + + // If `ur_in_source` is a member of `target_region`, OK. + // + // (This is implied by the loop below, actually, just an + // irresistible micro-opt. Mm. Premature optimization. So + // tasty.) + if self.inferred_values + .contains(self.target_region, ur_in_source) + { + continue; + } + + // If there is some other element X such that `target_region: X` and + // `X: ur_in_source`, OK. + if self.inferred_values + .universal_regions_outlived_by(self.target_region) + .any(|ur_in_target| { + self.universal_regions.outlives(ur_in_target, ur_in_source) + }) { + continue; + } + + // Otherwise, not known to be true. + return Err(self.elements.index(ur_in_source)); + } + + Ok(false) + } +} 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 c926c7432bb00..58e16e7673afc 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -12,18 +12,23 @@ use super::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; use rustc::infer::NLLRegionVariableOrigin; +use rustc::infer::RegionObligation; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; -use rustc::infer::region_constraints::VarOrigins; -use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir}; -use rustc::ty::{self, RegionVid}; +use rustc::infer::region_constraints::{GenericKind, VarOrigins}; +use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, + Location, Mir}; +use rustc::traits::ObligationCause; +use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_data_structures::fx::FxHashSet; use std::fmt; use std::rc::Rc; +use syntax::ast; use syntax_pos::Span; mod annotation; +mod dfs; +use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource}; mod dump_mir; mod graphviz; mod values; @@ -52,6 +57,9 @@ pub struct RegionInferenceContext<'tcx> { /// The constraints we have accumulated and used during solving. constraints: Vec, + /// Type constraints that we check after solving. + type_tests: Vec>, + /// Information about the universally quantified regions in scope /// on this function and their (known) relations to one another. universal_regions: UniversalRegions<'tcx>, @@ -94,6 +102,92 @@ pub struct Constraint { span: Span, } +/// A "type test" corresponds to an outlives constraint between a type +/// and a lifetime, like `T: 'x` or `::Bar: 'x`. They are +/// translated from the `Verify` region constraints in the ordinary +/// inference context. +/// +/// These sorts of constraints are handled differently than ordinary +/// constraints, at least at present. During type checking, the +/// `InferCtxt::process_registered_region_obligations` method will +/// attempt to convert a type test like `T: 'x` into an ordinary +/// outlives constraint when possible (for example, `&'a T: 'b` will +/// be converted into `'a: 'b` and registered as a `Constraint`). +/// +/// In some cases, however, there are outlives relationships that are +/// not converted into a region constraint, but rather into one of +/// these "type tests". The distinction is that a type test does not +/// influence the inference result, but instead just examines the +/// values that we ultimately inferred for each region variable and +/// checks that they meet certain extra criteria. If not, an error +/// can be issued. +/// +/// One reason for this is that these type tests typically boil down +/// to a check like `'a: 'x` where `'a` is a universally quantified +/// region -- and therefore not one whose value is really meant to be +/// *inferred*, precisely (this is not always the case: one can have a +/// type test like `>::Bar: 'x`, where `'?0` is an +/// inference variable). Another reason is that these type tests can +/// involve *disjunction* -- that is, they can be satisfied in more +/// than one way. +/// +/// For more information about this translation, see +/// `InferCtxt::process_registered_region_obligations` and +/// `InferCtxt::type_must_outlive` in `rustc::infer::outlives`. +#[derive(Clone, Debug)] +pub struct TypeTest<'tcx> { + /// The type `T` that must outlive the region. + pub generic_kind: GenericKind<'tcx>, + + /// The region `'x` that the type must outlive. + pub lower_bound: RegionVid, + + /// The point where the outlives relation must hold. + pub point: Location, + + /// Where did this constraint arise? + pub span: Span, + + /// A test which, if met by the region `'x`, proves that this type + /// constraint is satisfied. + pub test: RegionTest, +} + +/// A "test" that can be applied to some "subject region" `'x`. These are used to +/// describe type constraints. Tests do not presently affect the +/// region values that get inferred for each variable; they only +/// examine the results *after* inference. This means they can +/// conveniently include disjuction ("a or b must be true"). +#[derive(Clone, Debug)] +pub enum RegionTest { + /// The subject region `'x` must by outlived by *some* region in + /// the given set of regions. + /// + /// This test comes from e.g. a where clause like `T: 'a + 'b`, + /// which implies that we know that `T: 'a` and that `T: + /// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can + /// do so by showing that `'a: 'x` *or* `'b: 'x`. + IsOutlivedByAnyRegionIn(Vec), + + /// The subject region `'x` must by outlived by *all* regions in + /// the given set of regions. + /// + /// This test comes from e.g. a projection type like `T = >::Foo`, which must outlive `'a` or `'b`, and + /// maybe both. Therefore we can prove that `T: 'x` if we know + /// that `'a: 'x` *and* `'b: 'x`. + IsOutlivedByAllRegionsIn(Vec), + + /// Any of the given tests are true. + /// + /// This arises from projections, for which there are multiple + /// ways to prove an outlives relationship. + Any(Vec), + + /// All of the given tests are true. + All(Vec), +} + impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N @@ -121,6 +215,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { liveness_constraints: RegionValues::new(elements, num_region_variables), inferred_values: None, constraints: Vec::new(), + type_tests: Vec::new(), universal_regions, }; @@ -242,32 +337,414 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); } - /// Perform region inference. - pub(super) fn solve( + /// Add a "type test" that must be satisfied. + pub(super) fn add_type_test(&mut self, type_test: TypeTest<'tcx>) { + self.type_tests.push(type_test); + } + + /// Perform region inference and report errors if we see any + /// unsatisfiable constraints. If this is a closure, returns the + /// region requirements to propagate to our creator, if any. + pub(super) fn solve<'gcx>( &mut self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, mir_def_id: DefId, - ) -> Option { + ) -> 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); - // Now, see whether any of the constraints were too strong. In - // particular, we want to check for a case where a universally - // quantified region exceeded its bounds. Consider: - // - // fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - // - // In this case, returning `x` requires `&'a u32 <: &'b u32` - // and hence we establish (transitively) a constraint that - // `'a: 'b`. The `propagate_constraints` code above will - // therefore add `end('a)` into the region for `'b` -- but we - // have no evidence that `'a` outlives `'b`, so we want to report - // an error. + // If this is a closure, we can propagate unsatisfied + // `outlives_requirements` to our creator, so create a vector + // to store those. Otherwise, we'll pass in `None` to the + // functions below, which will trigger them to report errors + // eagerly. + let mut outlives_requirements = if infcx.tcx.is_closure(mir_def_id) { + Some(vec![]) + } else { + None + }; + + self.check_type_tests(infcx, mir, outlives_requirements.as_mut()); + + self.check_universal_regions(infcx, outlives_requirements.as_mut()); + + let outlives_requirements = outlives_requirements.unwrap_or(vec![]); + + if outlives_requirements.is_empty() { + None + } else { + let num_external_vids = self.universal_regions.num_global_and_external_regions(); + Some(ClosureRegionRequirements { + num_external_vids, + outlives_requirements, + }) + } + } + + /// 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 + /// feasible, but we check this later. + fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { + let mut changed = true; + + debug!("propagate_constraints()"); + debug!("propagate_constraints: constraints={:#?}", { + let mut constraints: Vec<_> = self.constraints.iter().collect(); + constraints.sort(); + constraints + }); + + // The initial values for each region are derived from the liveness + // constraints we have accumulated. + let mut inferred_values = self.liveness_constraints.clone(); + + while changed { + changed = false; + debug!("propagate_constraints: --------------------"); + for constraint in &self.constraints { + debug!("propagate_constraints: constraint={:?}", constraint); + + // Grow the value as needed to accommodate the + // outlives constraint. + let Ok(made_changes) = self.dfs( + mir, + CopyFromSourceToTarget { + source_region: constraint.sub, + target_region: constraint.sup, + inferred_values: &mut inferred_values, + constraint_point: constraint.point, + }, + ); + + if made_changes { + debug!("propagate_constraints: sub={:?}", constraint.sub); + debug!("propagate_constraints: sup={:?}", constraint.sup); + changed = true; + } + } + debug!("\n"); + } + + self.inferred_values = Some(inferred_values); + } + + /// Once regions have been propagated, this method is used to see + /// whether the "type tests" produced by typeck were satisfied; + /// type tests encode type-outlives relationships like `T: + /// 'a`. See `TypeTest` for more details. + fn check_type_tests<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + mut propagated_outlives_requirements: Option<&mut Vec>>, + ) { + let tcx = infcx.tcx; + + for type_test in &self.type_tests { + debug!("check_type_test: {:?}", type_test); + + if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) { + continue; + } + + if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { + if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) { + continue; + } + } + + // Oh the humanity. Obviously we will do better than this error eventually. + tcx.sess.span_err( + type_test.span, + &format!( + "`{}` does not outlive `{:?}`", + type_test.generic_kind, + type_test.lower_bound, + ), + ); + } + } + + fn try_promote_type_test<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + type_test: &TypeTest<'tcx>, + propagated_outlives_requirements: &mut Vec>, + ) -> bool { + let tcx = infcx.tcx; + + let TypeTest { + generic_kind, + lower_bound, + point: _, + span, + test: _, + } = type_test; + + let generic_ty = generic_kind.to_ty(tcx); + let subject = match self.try_promote_type_test_subject(infcx, generic_ty) { + Some(s) => s, + None => return false, + }; + + // Find some bounding subject-region R+ that is a super-region + // of the existing subject-region R. This should be a non-local, universal + // region, which ensures it can be encoded in a `ClosureOutlivesRequirement`. + let lower_bound_plus = self.non_local_universal_upper_bound(*lower_bound); + assert!(self.universal_regions.is_universal_region(lower_bound_plus)); + assert!(!self.universal_regions + .is_local_free_region(lower_bound_plus)); + + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject, + outlived_free_region: lower_bound_plus, + blame_span: *span, + }); + true + } + + /// When we promote a type test `T: 'r`, we have to convert the + /// type `T` into something we can store in a query result (so + /// something allocated for `'gcx`). This is problematic if `ty` + /// contains regions. During the course of NLL region checking, we + /// will have replaced all of those regions with fresh inference + /// variables. To create a test subject, we want to replace those + /// inference variables with some region from the closure + /// signature -- this is not always possible, so this is a + /// fallible process. Presuming we do find a suitable region, we + /// will represent it with a `ReClosureBound`, which is a + /// `RegionKind` variant that can be allocated in the gcx. + fn try_promote_type_test_subject<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + ty: Ty<'tcx>, + ) -> Option> { + let tcx = infcx.tcx; + let gcx = tcx.global_tcx(); + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + + debug!("try_promote_type_test_subject(ty = {:?})", ty); + + let ty = tcx.fold_regions(&ty, &mut false, |r, _depth| { + let region_vid = self.to_region_vid(r); + + // The challenge if this. We have some region variable `r` + // whose value is a set of CFG points and universal + // regions. We want to find if that set is *equivalent* to + // any of the named regions found in the closure. + // + // To do so, we compute the + // `non_local_universal_upper_bound`. This will be a + // non-local, universal region that is greater than `r`. + // However, it might not be *contained* within `r`, so + // then we further check whether this bound is contained + // in `r`. If so, we can say that `r` is equivalent to the + // bound. + // + // Let's work through a few examples. For these, imagine + // that we have 3 non-local regions (I'll denote them as + // `'static`, `'a`, and `'b`, though of course in the code + // they would be represented with indices) where: + // + // - `'static: 'a` + // - `'static: 'b` + // + // First, let's assume that `r` is some existential + // variable with an inferred value `{'a, 'static}` (plus + // some CFG nodes). In this case, the non-local upper + // bound is `'static`, since that outlives `'a`. `'static` + // is also a member of `r` and hence we consider `r` + // equivalent to `'static` (and replace it with + // `'static`). + // + // Now let's consider the inferred value `{'a, 'b}`. This + // means `r` is effectively `'a | 'b`. I'm not sure if + // this can come about, actually, but assuming it did, we + // would get a non-local upper bound of `'static`. Since + // `'static` is not contained in `r`, we would fail to + // find an equivalent. + let upper_bound = self.non_local_universal_upper_bound(region_vid); + if inferred_values.contains(region_vid, upper_bound) { + tcx.mk_region(ty::ReClosureBound(upper_bound)) + } else { + // In the case of a failure, use a `ReVar` + // result. This will cause the `lift` later on to + // fail. + r + } + }); + debug!("try_promote_type_test_subject: folded ty = {:?}", ty); + + // `lift` will only fail if we failed to promote some region. + let ty = gcx.lift(&ty)?; + + Some(ClosureOutlivesSubject::Ty(ty)) + } + + /// Given some universal or existential region `r`, finds a + /// non-local, universal region `r+` that outlives `r` at entry to (and + /// exit from) the closure. In the worst case, this will be + /// `'static`. + /// + /// This is used for two purposes. First, if we are propagated + /// some requirement `T: r`, we can use this method to enlarge `r` + /// to something we can encode for our creator (which only knows + /// about non-local, universal regions). It is also used when + /// encoding `T` as part of `try_promote_type_test_subject` (see + /// that fn for details). + /// + /// Since `r` is (potentially) an existential region, it has some + /// value which may include (a) any number of points in the CFG + /// and (b) any number of `end('x)` elements of universally + /// quantified regions. To convert this into a single universal + /// region we do as follows: + /// + /// - Ignore the CFG points in `'r`. All universally quantified regions + /// include the CFG anyhow. + /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding + /// a result `'y`. + /// - Finally, we take the non-local upper bound of `'y`. + /// - This uses `UniversalRegions::non_local_upper_bound`, which + /// is similar to this method but only works on universal + /// regions). + fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid { + let inferred_values = self.inferred_values.as_ref().unwrap(); + + debug!( + "non_local_universal_upper_bound(r={:?}={})", + r, + inferred_values.region_value_str(r) + ); + + // Find the smallest universal region that contains all other + // universal regions within `region`. + let mut lub = self.universal_regions.fr_fn_body; + for ur in inferred_values.universal_regions_outlived_by(r) { + lub = self.universal_regions.postdom_upper_bound(lub, ur); + } + + debug!("non_local_universal_upper_bound: lub={:?}", lub); + + // Grow further to get smallest universal region known to + // creator. + let non_local_lub = self.universal_regions.non_local_upper_bound(lub); + + debug!( + "non_local_universal_upper_bound: non_local_lub={:?}", + non_local_lub + ); + + non_local_lub + } + + /// Test if `test` is true when applied to `lower_bound` at + /// `point`, and returns true or false. + fn eval_region_test( + &self, + mir: &Mir<'tcx>, + point: Location, + lower_bound: RegionVid, + test: &RegionTest, + ) -> bool { + debug!( + "eval_region_test(point={:?}, lower_bound={:?}, test={:?})", + point, + lower_bound, + test + ); + match test { + RegionTest::IsOutlivedByAllRegionsIn(regions) => regions + .iter() + .all(|&r| self.eval_outlives(mir, r, lower_bound, point)), + + RegionTest::IsOutlivedByAnyRegionIn(regions) => regions + .iter() + .any(|&r| self.eval_outlives(mir, r, lower_bound, point)), + + RegionTest::Any(tests) => tests + .iter() + .any(|test| self.eval_region_test(mir, point, lower_bound, test)), + + RegionTest::All(tests) => tests + .iter() + .all(|test| self.eval_region_test(mir, point, lower_bound, test)), + } + } + + // Evaluate whether `sup_region: sub_region @ point`. + fn eval_outlives( + &self, + mir: &Mir<'tcx>, + sup_region: RegionVid, + sub_region: RegionVid, + point: Location, + ) -> bool { + debug!( + "eval_outlives({:?}: {:?} @ {:?})", + sup_region, + sub_region, + point + ); + + // Roughly speaking, do a DFS of all region elements reachable + // from `point` contained in `sub_region`. If any of those are + // *not* present in `sup_region`, the DFS will abort early and + // yield an `Err` result. + match self.dfs( + mir, + TestTargetOutlivesSource { + source_region: sub_region, + target_region: sup_region, + constraint_point: point, + elements: &self.elements, + universal_regions: &self.universal_regions, + inferred_values: self.inferred_values.as_ref().unwrap(), + }, + ) { + Ok(_) => { + debug!("eval_outlives: true"); + true + } + + Err(elem) => { + debug!( + "eval_outlives: false because `{:?}` is not present in `{:?}`", + self.elements.to_element(elem), + sup_region + ); + false + } + } + } + + /// Once regions have been propagated, this method is used to see + /// whether any of the constraints were too strong. In particular, + /// we want to check for a case where a universally quantified + /// region exceeded its bounds. Consider: + /// + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// + /// In this case, returning `x` requires `&'a u32 <: &'b u32` + /// and hence we establish (transitively) a constraint that + /// `'a: 'b`. The `propagate_constraints` code above will + /// therefore add `end('a)` into the region for `'b` -- but we + /// have no evidence that `'b` outlives `'a`, so we want to report + /// an error. + /// + /// If `propagated_outlives_requirements` is `Some`, then we will + /// push unsatisfied obligations into there. Otherwise, we'll + /// report them as errors. + fn check_universal_regions<'gcx>( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mut propagated_outlives_requirements: Option<&mut Vec>>, + ) { // The universal regions are always found in a prefix of the // full list. let universal_definitions = self.definitions @@ -277,32 +754,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // 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); + self.check_universal_region(infcx, fr, &mut propagated_outlives_requirements); } - - // If this is not a closure, then there is no caller to which we can - // "pass the buck". So if there are any outlives-requirements that were - // not satisfied, we just have to report a hard error here. - if !tcx.is_closure(mir_def_id) { - for outlives_requirement in outlives_requirements { - self.report_error( - infcx, - outlives_requirement.free_region, - outlives_requirement.outlived_free_region, - outlives_requirement.blame_span, - ); - } - return None; - } - - let num_external_vids = self.universal_regions.num_global_and_external_regions(); - - Some(ClosureRegionRequirements { - num_external_vids, - outlives_requirements, - }) } /// Check the final value for the free region `fr` to see if it @@ -313,11 +767,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Things that are to be propagated are accumulated into the /// `outlives_requirements` vector. - fn check_universal_region( + fn check_universal_region<'gcx>( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, longer_fr: RegionVid, - outlives_requirements: &mut Vec, + propagated_outlives_requirements: &mut Option<&mut Vec>>, ) { let inferred_values = self.inferred_values.as_ref().unwrap(); @@ -339,33 +793,39 @@ impl<'tcx> RegionInferenceContext<'tcx> { 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 let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + // 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+` + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::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. + // If we are not in a context where we can propagate + // errors, or we could not shrink `fr` to something + // smaller, then just report an error. + // + // Note: in this case, we use the unapproximated regions + // to report the error. This gives better error messages + // in some cases. self.report_error(infcx, longer_fr, shorter_fr, blame_span); } } @@ -395,117 +855,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } - /// 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 - /// feasible, but we check this later. - fn propagate_constraints(&mut self, mir: &Mir<'tcx>) { - let mut changed = true; - - debug!("propagate_constraints()"); - debug!("propagate_constraints: constraints={:#?}", { - let mut constraints: Vec<_> = self.constraints.iter().collect(); - constraints.sort(); - constraints - }); - - // The initial values for each region are derived from the liveness - // constraints we have accumulated. - let mut inferred_values = self.liveness_constraints.clone(); - - while changed { - changed = false; - debug!("propagate_constraints: --------------------"); - for constraint in &self.constraints { - debug!("propagate_constraints: constraint={:?}", constraint); - - // Grow the value as needed to accommodate the - // outlives constraint. - - if self.copy( - &mut inferred_values, - mir, - constraint.sub, - constraint.sup, - constraint.point, - ) { - debug!("propagate_constraints: sub={:?}", constraint.sub); - debug!("propagate_constraints: sup={:?}", constraint.sup); - changed = true; - } - } - debug!("\n"); - } - - self.inferred_values = Some(inferred_values); - } - - fn copy( - &self, - inferred_values: &mut RegionValues, - mir: &Mir<'tcx>, - from_region: RegionVid, - to_region: RegionVid, - constraint_point: Location, - ) -> bool { - let mut changed = false; - - let mut stack = vec![]; - let mut visited = FxHashSet(); - - stack.push(constraint_point); - while let Some(p) = stack.pop() { - let point_index = self.elements.index(p); - - if !inferred_values.contains(from_region, point_index) { - debug!(" not in from-region"); - continue; - } - - if !visited.insert(p) { - debug!(" already visited"); - continue; - } - - let new = inferred_values.add(to_region, point_index); - changed |= new; - - let block_data = &mir[p.block]; - let successor_points = if p.statement_index < block_data.statements.len() { - vec![ - Location { - statement_index: p.statement_index + 1, - ..p - }, - ] - } else { - block_data - .terminator() - .successors() - .iter() - .map(|&basic_block| { - Location { - statement_index: 0, - block: basic_block, - } - }) - .collect::>() - }; - - if successor_points.is_empty() { - // If we reach the END point in the graph, then copy - // over any skolemized end points in the `from_region` - // and make sure they are included in the `to_region`. - changed |= - inferred_values.add_universal_regions_outlived_by(from_region, to_region); - } else { - stack.extend(successor_points); - } - } - - changed - } - /// Tries to finds a good span to blame for the fact that `fr1` /// contains `fr2`. fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { @@ -520,35 +869,50 @@ impl<'tcx> RegionInferenceContext<'tcx> { // be obvious to the user -- not to mention the naive notion // of dependencies, which doesn't account for the locations of // contraints at all. But it will do for now. - for constraint in &self.constraints { - if constraint.sub == fr2 && influenced_fr1[constraint.sup] { - return constraint.span; - } - } - - bug!( - "could not find any constraint to blame for {:?}: {:?}", - fr1, - fr2 - ); + let relevant_constraint = self.constraints + .iter() + .filter_map(|constraint| { + if constraint.sub != fr2 { + None + } else { + influenced_fr1[constraint.sup] + .map(|distance| (distance, constraint.span)) + } + }) + .min() // constraining fr1 with fewer hops *ought* to be more obvious + .map(|(_dist, span)| span); + + relevant_constraint.unwrap_or_else(|| { + bug!( + "could not find any constraint to blame for {:?}: {:?}", + fr1, + fr2 + ); + }) } /// Finds all regions whose values `'a` may depend on in some way. - /// Basically if there exists a constraint `'a: 'b @ P`, then `'b` - /// and `dependencies('b)` will be in the final set. + /// For each region, returns either `None` (does not influence + /// `'a`) or `Some(d)` which indicates that it influences `'a` + /// with distinct `d` (minimum number of edges that must be + /// traversed). /// /// Used during error reporting, extremely naive and inefficient. - fn dependencies(&self, r0: RegionVid) -> IndexVec { - let mut result_set = IndexVec::from_elem(false, &self.definitions); + fn dependencies(&self, r0: RegionVid) -> IndexVec> { + let mut result_set = IndexVec::from_elem(None, &self.definitions); let mut changed = true; - result_set[r0] = true; + result_set[r0] = Some(0); // distance 0 from `r0` while changed { changed = false; for constraint in &self.constraints { - if result_set[constraint.sup] { - if !result_set[constraint.sub] { - result_set[constraint.sub] = true; + if let Some(n) = result_set[constraint.sup] { + let m = n + 1; + if result_set[constraint.sub] + .map(|distance| m < distance) + .unwrap_or(true) + { + result_set[constraint.sub] = Some(m); changed = true; } } @@ -585,17 +949,27 @@ impl fmt::Debug for Constraint { } } -pub trait ClosureRegionRequirementsExt { - fn apply_requirements<'tcx>( +pub trait ClosureRegionRequirementsExt<'gcx, 'tcx> { + fn apply_requirements( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, location: Location, closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, ); + + fn subst_closure_mapping( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + closure_mapping: &IndexVec>, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx>; } -impl ClosureRegionRequirementsExt for ClosureRegionRequirements { +impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequirements<'gcx> { /// 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 @@ -608,9 +982,10 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { /// 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>( + fn apply_requirements( &self, - infcx: &InferCtxt<'_, '_, 'tcx>, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, location: Location, closure_def_id: DefId, closure_substs: ty::ClosureSubsts<'tcx>, @@ -632,22 +1007,71 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { // 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); + &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); + + match outlives_requirement.subject { + ClosureOutlivesSubject::Region(region) => { + let region = closure_mapping[region]; + debug!( + "apply_requirements: region={:?} \ + outlived_region={:?} \ + outlives_requirement={:?}", + region, + outlived_region, + outlives_requirement, + ); + infcx.sub_regions(origin, outlived_region, region); + } + + ClosureOutlivesSubject::Ty(ty) => { + let ty = self.subst_closure_mapping(infcx, closure_mapping, &ty); + debug!( + "apply_requirements: ty={:?} \ + outlived_region={:?} \ + outlives_requirement={:?}", + ty, + outlived_region, + outlives_requirement, + ); + infcx.register_region_obligation( + body_id, + RegionObligation { + sup_type: ty, + sub_region: outlived_region, + cause: ObligationCause::misc(outlives_requirement.blame_span, body_id), + }, + ); + } + } } } + + fn subst_closure_mapping( + &self, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + closure_mapping: &IndexVec>, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx>, + { + infcx.tcx.fold_regions(value, &mut false, |r, _depth| { + if let ty::ReClosureBound(vid) = r { + closure_mapping[*vid] + } else { + bug!( + "subst_closure_mapping: encountered non-closure bound free region {:?}", + r + ) + } + }) + } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index 849ccd3259a24..5f23a0e5790af 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -215,8 +215,11 @@ impl RegionValues { // FIXME. We could optimize this by improving // `BitMatrix::merge` so it does not always merge an entire // row. - debug!("add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", - from_region, to_region); + debug!( + "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", + from_region, + to_region + ); let mut changed = false; for elem in self.elements.all_universal_region_indices() { if self.contains(from_region, elem) { @@ -269,24 +272,70 @@ impl RegionValues { let mut result = String::new(); result.push_str("{"); - for (index, element) in self.elements_contained_in(r).enumerate() { - if index > 0 { - result.push_str(", "); - } + // Set to Some(l1, l2) when we have observed all the locations + // from l1..=l2 (inclusive) but not yet printed them. This + // gets extended if we then see l3 where l3 is the successor + // to l2. + let mut open_location: Option<(Location, Location)> = None; + + let mut sep = ""; + let mut push_sep = |s: &mut String| { + s.push_str(sep); + sep = ", "; + }; + for element in self.elements_contained_in(r) { match element { RegionElement::Location(l) => { - result.push_str(&format!("{:?}", l)); + if let Some((location1, location2)) = open_location { + if location2.block == l.block + && location2.statement_index == l.statement_index - 1 + { + open_location = Some((location1, l)); + continue; + } + + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + } + + open_location = Some((l, l)); } RegionElement::UniversalRegion(fr) => { + if let Some((location1, location2)) = open_location { + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + open_location = None; + } + + push_sep(&mut result); result.push_str(&format!("{:?}", fr)); } } } + if let Some((location1, location2)) = open_location { + push_sep(&mut result); + Self::push_location_range(&mut result, location1, location2); + } + result.push_str("}"); result } + + fn push_location_range(str: &mut String, location1: Location, location2: Location) { + if location1 == location2 { + str.push_str(&format!("{:?}", location1)); + } else { + assert_eq!(location1.block, location2.block); + str.push_str(&format!( + "{:?}[{}..={}]", + location1.block, + location1.statement_index, + location2.statement_index + )); + } + } } diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 1262c238a132c..79505405692d8 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -8,50 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc_data_structures::indexed_vec::Idx; use rustc::ty::subst::Substs; use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable}; -use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind}; -use rustc::mir::RETURN_PLACE; +use rustc::mir::{BasicBlock, Location, Mir, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; -use super::ToRegionVid; -use super::universal_regions::UniversalRegions; - /// Replaces all free regions appearing in the MIR with fresh /// inference variables, returning the number of variables created. -pub fn renumber_mir<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - universal_regions: &UniversalRegions<'tcx>, - mir: &mut Mir<'tcx>, -) { +pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut Mir<'tcx>) { debug!("renumber_mir()"); 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, - arg_count: mir.arg_count, - }; + let mut visitor = NLLVisitor { infcx }; visitor.visit_mir(mir); } struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - arg_count: usize, } impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { @@ -71,45 +45,13 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { self.infcx.next_nll_region_var(origin) }) } - - /// 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!("assert_free_regions_are_renumbered(value={:?})", value); - - 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 { - // The first argument is return slot, next N are arguments. - local.index() <= self.arg_count - } } impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { - let is_arg = match ty_context { - TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local), - TyContext::ReturnTy(..) => true, - TyContext::Location(..) => false, - }; - debug!( - "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})", - ty, - is_arg, - ty_context - ); + debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context); - if is_arg { - self.assert_free_regions_are_renumbered(ty); - } else { - *ty = self.renumber_regions(ty_context, 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 c98a94fa8bc10..e42302761bfa3 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -11,11 +11,14 @@ use rustc::mir::Mir; use rustc::infer::region_constraints::Constraint; use rustc::infer::region_constraints::RegionConstraintData; +use rustc::infer::region_constraints::{Verify, VerifyBound}; use rustc::ty; -use transform::type_check::MirTypeckRegionConstraints; -use transform::type_check::OutlivesSet; +use syntax::codemap::Span; -use super::region_infer::RegionInferenceContext; +use super::region_infer::{TypeTest, RegionInferenceContext, RegionTest}; +use super::type_check::Locations; +use super::type_check::MirTypeckRegionConstraints; +use super::type_check::OutlivesSet; /// When the MIR type-checker executes, it validates all the types in /// the MIR, and in the process generates a set of constraints that @@ -27,10 +30,7 @@ pub(super) fn generate<'tcx>( mir: &Mir<'tcx>, constraints: &MirTypeckRegionConstraints<'tcx>, ) { - SubtypeConstraintGenerator { - regioncx, - mir, - }.generate(constraints); + SubtypeConstraintGenerator { regioncx, mir }.generate(constraints); } struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> { @@ -65,6 +65,8 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { givens, } = data; + let span = self.mir.source_info(locations.from_location).span; + for constraint in constraints.keys() { debug!("generate: constraint: {:?}", constraint); let (a_vid, b_vid) = match constraint { @@ -81,12 +83,15 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { // reverse direction, because `regioncx` talks about // "outlives" (`>=`) whereas the region constraints // talk about `<=`. - let span = self.mir.source_info(locations.from_location).span; self.regioncx .add_outlives(span, b_vid, a_vid, locations.at_location); } - assert!(verifys.is_empty(), "verifys not yet implemented"); + for verify in verifys { + let type_test = self.verify_to_type_test(verify, span, locations); + self.regioncx.add_type_test(type_test); + } + assert!( givens.is_empty(), "MIR type-checker does not use givens (thank goodness)" @@ -94,6 +99,55 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { } } + fn verify_to_type_test( + &self, + verify: &Verify<'tcx>, + span: Span, + locations: &Locations, + ) -> TypeTest<'tcx> { + let generic_kind = verify.kind; + + let lower_bound = self.to_region_vid(verify.region); + + let point = locations.at_location; + + let test = self.verify_bound_to_region_test(&verify.bound); + + TypeTest { + generic_kind, + lower_bound, + point, + span, + test, + } + } + + fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest { + match verify_bound { + VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn( + regions.iter().map(|r| self.to_region_vid(r)).collect(), + ), + + VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn( + regions.iter().map(|r| self.to_region_vid(r)).collect(), + ), + + VerifyBound::AnyBound(bounds) => RegionTest::Any( + bounds + .iter() + .map(|b| self.verify_bound_to_region_test(b)) + .collect(), + ), + + VerifyBound::AllBounds(bounds) => RegionTest::All( + bounds + .iter() + .map(|b| self.verify_bound_to_region_test(b)) + .collect(), + ), + } + } + fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { // Every region that we see in the constraints came from the // MIR or from the parameter environment. If the former, it diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs new file mode 100644 index 0000000000000..e41bf7cda8e6c --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -0,0 +1,220 @@ +// 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. + +use dataflow::{FlowAtLocation, FlowsAtLocation}; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::{HasMoveData, MoveData}; +use rustc::mir::{BasicBlock, Location, Mir}; +use rustc::mir::Local; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::util::common::ErrorReported; +use rustc_data_structures::fx::FxHashSet; +use syntax::codemap::DUMMY_SP; +use util::liveness::LivenessResults; + +use super::TypeChecker; + +/// Combines liveness analysis with initialization analysis to +/// determine which variables are live at which points, both due to +/// ordinary uses and drops. Returns a set of (ty, location) pairs +/// that indicate which types must be live at which point in the CFG. +/// This vector is consumed by `constraint_generation`. +/// +/// NB. This computation requires normalization; therefore, it must be +/// performed before +pub(super) fn generate<'gcx, 'tcx>( + cx: &mut TypeChecker<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + liveness: &LivenessResults, + flow_inits: &mut FlowAtLocation>, + move_data: &MoveData<'tcx>, +) { + let tcx = cx.tcx(); + let mut generator = TypeLivenessGenerator { + cx, + tcx, + mir, + liveness, + flow_inits, + move_data, + }; + + for bb in mir.basic_blocks().indices() { + generator.add_liveness_constraints(bb); + } +} + +struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> +where + 'typeck: 'gen, + 'flow: 'gen, + 'tcx: 'typeck + 'flow, + 'gcx: 'tcx, +{ + cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>, + tcx: TyCtxt<'typeck, 'gcx, 'tcx>, + mir: &'gen Mir<'tcx>, + liveness: &'gen LivenessResults, + flow_inits: &'gen mut FlowAtLocation>, + move_data: &'gen MoveData<'tcx>, +} + +impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> { + /// Liveness constraints: + /// + /// > If a variable V is live at point P, then all regions R in the type of V + /// > must include the point P. + fn add_liveness_constraints(&mut self, bb: BasicBlock) { + debug!("add_liveness_constraints(bb={:?})", bb); + + self.liveness + .regular + .simulate_block(self.mir, bb, |location, live_locals| { + for live_local in live_locals.iter() { + let live_local_ty = self.mir.local_decls[live_local].ty; + self.push_type_live_constraint(live_local_ty, location); + } + }); + + let mut all_live_locals: Vec<(Location, Vec)> = vec![]; + self.liveness + .drop + .simulate_block(self.mir, bb, |location, live_locals| { + all_live_locals.push((location, live_locals.iter().collect())); + }); + debug!( + "add_liveness_constraints: all_live_locals={:#?}", + all_live_locals + ); + + let terminator_index = self.mir.basic_blocks()[bb].statements.len(); + self.flow_inits.reset_to_entry_of(bb); + while let Some((location, live_locals)) = all_live_locals.pop() { + for live_local in live_locals { + debug!( + "add_liveness_constraints: location={:?} live_local={:?}", + location, + live_local + ); + + self.flow_inits.each_state_bit(|mpi_init| { + debug!( + "add_liveness_constraints: location={:?} initialized={:?}", + location, + &self.flow_inits.operator().move_data().move_paths[mpi_init] + ); + }); + + let mpi = self.move_data.rev_lookup.find_local(live_local); + if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) { + debug!( + "add_liveness_constraints: mpi={:?} has initialized child {:?}", + self.move_data.move_paths[mpi], + self.move_data.move_paths[initialized_child] + ); + + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_drop_live_constraint(live_local_ty, location); + } + } + + if location.statement_index == terminator_index { + debug!( + "add_liveness_constraints: reconstruct_terminator_effect from {:#?}", + location + ); + self.flow_inits.reconstruct_terminator_effect(location); + } else { + debug!( + "add_liveness_constraints: reconstruct_statement_effect from {:#?}", + location + ); + self.flow_inits.reconstruct_statement_effect(location); + } + self.flow_inits.apply_local_effect(location); + } + } + + /// Some variable with type `live_ty` is "regular live" at + /// `location` -- i.e., it may be used later. This means that all + /// regions appearing in the type `live_ty` must be live at + /// `location`. + fn push_type_live_constraint(&mut self, value: T, location: Location) + where + T: TypeFoldable<'tcx>, + { + debug!( + "push_type_live_constraint(live_ty={:?}, location={:?})", + value, + location + ); + + self.tcx.for_each_free_region(&value, |live_region| { + self.cx + .constraints + .liveness_set + .push((live_region, location)); + }); + } + + /// Some variable with type `live_ty` is "drop live" at `location` + /// -- i.e., it may be dropped later. This means that *some* of + /// the regions in its type must be live at `location`. The + /// precise set will depend on the dropck constraints, and in + /// particular this takes `#[may_dangle]` into account. + fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { + debug!( + "add_drop_live_constraint(dropped_ty={:?}, location={:?})", + dropped_ty, + location + ); + + let tcx = self.cx.infcx.tcx; + let mut types = vec![(dropped_ty, 0)]; + let mut known = FxHashSet(); + while let Some((ty, depth)) = types.pop() { + let span = DUMMY_SP; // FIXME + let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { + Ok(result) => result, + Err(ErrorReported) => { + continue; + } + }; + + let ty::DtorckConstraint { + outlives, + dtorck_types, + } = result; + + // All things in the `outlives` array may be touched by + // the destructor and must be live at this point. + for outlive in outlives { + self.push_type_live_constraint(outlive, location); + } + + // However, there may also be some types that + // `dtorck_constraint_for_ty` could not resolve (e.g., + // associated types and parameters). We need to normalize + // associated types here and possibly recursively process. + for ty in dtorck_types { + let ty = self.cx.normalize(&ty, location); + match ty.sty { + ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { + self.push_type_live_constraint(ty, location); + } + + _ => if known.insert(ty) { + types.push((ty, depth + 1)); + }, + } + } + } + } +} diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs similarity index 93% rename from src/librustc_mir/transform/type_check.rs rename to src/librustc_mir/borrow_check/nll/type_check/mod.rs index fae911780a1e6..6cdd77048c998 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -12,6 +12,9 @@ #![allow(unreachable_code)] use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; +use dataflow::FlowAtLocation; +use dataflow::MaybeInitializedLvals; +use dataflow::move_paths::MoveData; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; use rustc::infer::region_constraints::RegionConstraintData; use rustc::traits::{self, FulfillmentContext}; @@ -26,24 +29,80 @@ use std::fmt; use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; use transform::{MirPass, MirSource}; +use util::liveness::LivenessResults; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::Idx; +mod liveness; + /// Type checks the given `mir` in the context of the inference /// context `infcx`. Returns any region constraints that have yet to -/// be proven. +/// be proven. This result is includes liveness constraints that +/// ensure that regions appearing in the types of all local variables +/// are live at all points where that local variable may later be +/// used. /// /// This phase of type-check ought to be infallible -- this is because /// the original, HIR-based type-check succeeded. So if any errors /// occur here, we will get a `bug!` reported. -pub fn type_check<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, +/// +/// # Parameters +/// +/// - `infcx` -- inference context to use +/// - `body_id` -- body-id of the MIR being checked +/// - `param_env` -- parameter environment to use for trait solving +/// - `mir` -- MIR to type-check +/// - `implicit_region_bound` -- a region which all generic parameters are assumed +/// to outlive; should represent the fn body +/// - `input_tys` -- fully liberated, but **not** normalized, expected types of the arguments; +/// the types of the input parameters found in the MIR itself will be equated with these +/// - `output_ty` -- fully liberaetd, but **not** normalized, expected return type; +/// the type for the RETURN_PLACE will be equated with this +/// - `liveness` -- results of a liveness computation on the MIR; used to create liveness +/// constraints for the regions in the types of variables +/// - `flow_inits` -- results of a maybe-init dataflow analysis +/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis +pub(crate) fn type_check<'gcx, 'tcx>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + implicit_region_bound: ty::Region<'tcx>, + input_tys: &[Ty<'tcx>], + output_ty: Ty<'tcx>, + liveness: &LivenessResults, + flow_inits: &mut FlowAtLocation>, + move_data: &MoveData<'tcx>, ) -> MirTypeckRegionConstraints<'tcx> { - let mut checker = TypeChecker::new(infcx, body_id, param_env); + type_check_internal( + infcx, + body_id, + param_env, + mir, + Some(implicit_region_bound), + &mut |cx| { + liveness::generate(cx, mir, liveness, flow_inits, move_data); + + // Equate the input and output tys given by the user with + // the ones found in the MIR. + cx.equate_input_or_output(output_ty, mir.local_decls[RETURN_PLACE].ty); + for (&input_ty, local) in input_tys.iter().zip((1..).map(Local::new)) { + cx.equate_input_or_output(input_ty, mir.local_decls[local].ty); + } + }, + ) +} + +fn type_check_internal<'gcx, 'tcx>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + mir: &Mir<'tcx>, + implicit_region_bound: Option>, + extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), +) -> MirTypeckRegionConstraints<'tcx> { + let mut checker = TypeChecker::new(infcx, body_id, param_env, implicit_region_bound); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, mir); verifier.visit_mir(mir); @@ -55,9 +114,12 @@ pub fn type_check<'a, 'gcx, 'tcx>( checker.typeck_mir(mir); } + extra(&mut checker); + checker.constraints } + fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { // We sometimes see MIR failures (notably predicate failures) due to // the fact that we check rvalue sized predicates here. So use `delay_span_bug` @@ -503,11 +565,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { /// constraints needed for it to be valid and well-typed. Along the /// way, it accrues region constraints -- these can later be used by /// NLL region checking. -pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { +struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'gcx>, last_span: Span, body_id: ast::NodeId, + implicit_region_bound: Option>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, constraints: MirTypeckRegionConstraints<'tcx>, } @@ -561,12 +624,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, + implicit_region_bound: Option>, ) -> Self { TypeChecker { infcx, last_span: DUMMY_SP, body_id, param_env, + implicit_region_bound, reported_errors: FxHashSet(), constraints: MirTypeckRegionConstraints::default(), } @@ -591,8 +656,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { span_mirbug!(self, "", "errors selecting obligation: {:?}", e); } - self.infcx - .process_registered_region_obligations(&[], None, self.param_env, self.body_id); + self.infcx.process_registered_region_obligations( + &[], + self.implicit_region_bound, + self.param_env, + self.body_id, + ); let data = self.infcx.take_and_reset_region_constraints(); if !data.is_empty() { @@ -625,6 +694,25 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }) } + fn equate_input_or_output(&mut self, unnormalized_a: Ty<'tcx>, b: Ty<'tcx>) { + let start_position = Location { + block: START_BLOCK, + statement_index: 0, + }; + let a = self.normalize(&unnormalized_a, start_position); + if let Err(terr) = self.eq_types(a, b, start_position.at_self()) { + span_mirbug!( + self, + start_position, + "bad input or output {:?} normalized to {:?} should equal {:?} but got error {:?}", + unnormalized_a, + a, + b, + terr + ); + } + } + fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.infcx.tcx } @@ -1368,6 +1456,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) { closure_region_requirements.apply_requirements( self.infcx, + self.body_id, location, *def_id, *substs, @@ -1474,7 +1563,7 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _region_constraint_sets = type_check(&infcx, id, param_env, mir); + let _ = type_check_internal(&infcx, id, param_env, mir, None, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 5336bd271f590..a1e6ea135c68d 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -45,6 +45,11 @@ pub struct UniversalRegions<'tcx> { /// The vid assigned to `'static` pub fr_static: RegionVid, + /// A special region vid created to represent the current MIR fn + /// body. It will outlive the entire CFG but it will not outlive + /// any other universal regions. + pub fr_fn_body: 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: @@ -64,12 +69,16 @@ pub struct UniversalRegions<'tcx> { /// 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. + /// The return type of this function, with all regions replaced by + /// their universal `RegionVid` equivalents. This type is **NOT + /// NORMALIZED** (i.e., it contains unnormalized associated type + /// projections). pub output_ty: Ty<'tcx>, /// The fully liberated input types of this function, with all /// regions replaced by their universal `RegionVid` equivalents. + /// This type is **NOT NORMALIZED** (i.e., it contains + /// unnormalized associated type projections). pub input_tys: &'tcx [Ty<'tcx>], /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to @@ -242,17 +251,7 @@ impl<'tcx> UniversalRegions<'tcx> { (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. + /// True if `r` is classified as an local region. pub fn is_local_free_region(&self, r: RegionVid) -> bool { self.region_classification(r) == Some(RegionClassification::Local) } @@ -262,6 +261,20 @@ impl<'tcx> UniversalRegions<'tcx> { self.num_universals } + /// Given two universal regions, returns the postdominating + /// upper-bound (effectively the least upper bound). + /// + /// (See `TransitiveRelation::postdom_upper_bound` for details on + /// the postdominating upper bound in general.) + pub fn postdom_upper_bound(&self, fr1: RegionVid, fr2: RegionVid) -> RegionVid { + assert!(self.is_universal_region(fr1)); + assert!(self.is_universal_region(fr2)); + *self.relations + .inverse_outlives + .postdom_upper_bound(&fr1, &fr2) + .unwrap_or(&self.fr_static) + } + /// 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 @@ -305,6 +318,10 @@ impl<'tcx> UniversalRegions<'tcx> { relation: &TransitiveRelation, fr0: RegionVid, ) -> Option { + // This method assumes that `fr0` is one of the universally + // quantified region variables. + assert!(self.is_universal_region(fr0)); + let mut external_parents = vec![]; let mut queue = vec![&fr0]; @@ -408,6 +425,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { 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 fr_fn_body = self.infcx.next_nll_region_var(FR).to_region_vid(); let num_universals = self.infcx.num_region_vars(); // Insert the facts we know from the predicates. Why? Why not. @@ -419,12 +437,19 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { self.add_implied_bounds(&indices, ty); } - // Finally, outlives is reflexive, and static outlives every - // other free region. + // Finally: + // - outlives is reflexive, so `'r: 'r` for every region `'r` + // - `'static: 'r` for every region `'r` + // - `'r: 'fn_body` for every (other) universally quantified + // region `'r`, all of which are provided by our caller for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) { - debug!("build: relating free region {:?} to itself and to 'static", fr); + 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); + self.relations.relate_universal_regions(fr, fr_fn_body); } let (output_ty, input_tys) = inputs_and_output.split_last().unwrap(); @@ -432,19 +457,26 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { // 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); + 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, + fr_fn_body, first_extern_index, first_local_index, num_universals, diff --git a/src/librustc_mir/dataflow/at_location.rs b/src/librustc_mir/dataflow/at_location.rs index a6c3398489ad7..7f243ad6e264c 100644 --- a/src/librustc_mir/dataflow/at_location.rs +++ b/src/librustc_mir/dataflow/at_location.rs @@ -25,26 +25,43 @@ use std::iter; /// There's probably a way to auto-impl this, but I think /// it is cleaner to have manual visitor impls. pub trait FlowsAtLocation { - // reset the state bitvector to represent the entry to block `bb`. + /// Reset the state bitvector to represent the entry to block `bb`. fn reset_to_entry_of(&mut self, bb: BasicBlock); - // build gen + kill sets for statement at `loc`. + /// Build gen + kill sets for statement at `loc`. + /// + /// Note that invoking this method alone does not change the + /// `curr_state` -- you must invoke `apply_local_effect` + /// afterwards. fn reconstruct_statement_effect(&mut self, loc: Location); - // build gen + kill sets for terminator for `loc`. + /// Build gen + kill sets for terminator for `loc`. + /// + /// Note that invoking this method alone does not change the + /// `curr_state` -- you must invoke `apply_local_effect` + /// afterwards. fn reconstruct_terminator_effect(&mut self, loc: Location); - // apply current gen + kill sets to `flow_state`. - // - // (`bb` and `stmt_idx` parameters can be ignored if desired by - // client. For the terminator, the `stmt_idx` will be the number - // of statements in the block.) + /// Apply current gen + kill sets to `flow_state`. + /// + /// (`loc` parameters can be ignored if desired by + /// client. For the terminator, the `stmt_idx` will be the number + /// of statements in the block.) fn apply_local_effect(&mut self, loc: Location); } /// Represents the state of dataflow at a particular /// CFG location, both before and after it is /// executed. +/// +/// Data flow results are typically computed only as basic block +/// boundaries. A `FlowInProgress` allows you to reconstruct the +/// effects at any point in the control-flow graph by starting with +/// the state at the start of the basic block (`reset_to_entry_of`) +/// and then replaying the effects of statements and terminators +/// (e.g. via `reconstruct_statement_effect` and +/// `reconstruct_terminator_effect`; don't forget to call +/// `apply_local_effect`). pub struct FlowAtLocation where BD: BitDenotation, @@ -59,6 +76,7 @@ impl FlowAtLocation where BD: BitDenotation, { + /// Iterate over each bit set in the current state. pub fn each_state_bit(&self, f: F) where F: FnMut(BD::Idx), @@ -67,6 +85,9 @@ where .each_bit(self.base_results.operator().bits_per_block(), f) } + /// Iterate over each `gen` bit in the current effect (invoke + /// `reconstruct_statement_effect` or + /// `reconstruct_terminator_effect` first). pub fn each_gen_bit(&self, f: F) where F: FnMut(BD::Idx), @@ -88,6 +109,7 @@ where } } + /// Access the underlying operator. pub fn operator(&self) -> &BD { self.base_results.operator() } @@ -96,11 +118,15 @@ where self.curr_state.contains(x) } + /// Returns an iterator over the elements present in the current state. pub fn elems_incoming(&self) -> iter::Peekable> { let univ = self.base_results.sets().bits_per_block(); self.curr_state.elems(univ).peekable() } + /// Creates a clone of the current state and applies the local + /// effects to the clone (leaving the state of self intact). + /// Invokes `f` with an iterator over the resulting state. pub fn with_elems_outgoing(&self, f: F) where F: FnOnce(indexed_set::Elems), diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index fb9daf07c71dc..563405fccc970 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use borrow_check::nll::type_check; use build; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::mir::{Mir, Promoted}; @@ -30,7 +31,6 @@ pub mod simplify_branches; pub mod simplify; pub mod erase_regions; pub mod no_landing_pads; -pub mod type_check; pub mod rustc_peek; pub mod elaborate_drops; pub mod add_call_guards; diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 45c3fcd8a615d..5163f74dd2529 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -80,6 +80,39 @@ pub struct LivenessMode { pub include_drops: bool, } +/// A combination of liveness results, used in NLL. +pub struct LivenessResults { + /// Liveness results where a regular use makes a variable X live, + /// but not a drop. + pub regular: LivenessResult, + + /// Liveness results where a drop makes a variable X live, + /// but not a regular use. + pub drop: LivenessResult, +} + +impl LivenessResults { + pub fn compute<'tcx>(mir: &Mir<'tcx>) -> LivenessResults { + LivenessResults { + regular: liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: true, + include_drops: false, + }, + ), + + drop: liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: false, + include_drops: true, + }, + ), + } + } +} + /// Compute which local variables are live within the given function /// `mir`. The liveness mode `mode` determines what sorts of uses are /// considered to make a variable live (e.g., do drops count?). diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index ef6552c8e33f4..df42d5eaa0a3d 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -465,6 +465,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::ReFree(..) | + ty::ReClosureBound(..) | ty::ReScope(..) | ty::ReVar(..) | ty::ReSkolemized(..) | diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 91908de98a65d..c7657c9b2ff45 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1055,6 +1055,7 @@ impl Clean> for ty::RegionKind { ty::ReVar(..) | ty::ReSkolemized(..) | ty::ReEmpty | + ty::ReClosureBound(_) | ty::ReErased => None } } diff --git a/src/test/compile-fail/mir_check_cast_closure.rs b/src/test/compile-fail/mir_check_cast_closure.rs index be0d4b1374185..6562efeb6d893 100644 --- a/src/test/compile-fail/mir_check_cast_closure.rs +++ b/src/test/compile-fail/mir_check_cast_closure.rs @@ -14,9 +14,9 @@ fn bar<'a, 'b>() -> fn(&'a u32, &'b u32) -> &'a u32 { let g: fn(_, _) -> _ = |_x, y| y; + //~^ ERROR free region `'b` does not outlive free region `'a` g //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `'b` does not outlive free region `'a` } fn main() {} diff --git a/src/test/compile-fail/mir_check_cast_reify.rs b/src/test/compile-fail/mir_check_cast_reify.rs index 091e0b71b2dc3..1736aea2d6de7 100644 --- a/src/test/compile-fail/mir_check_cast_reify.rs +++ b/src/test/compile-fail/mir_check_cast_reify.rs @@ -45,8 +45,8 @@ fn bar<'a>(x: &'a u32) -> &'static u32 { // as part of checking the `ReifyFnPointer`. let f: fn(_) -> _ = foo; //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `'_#1r` does not outlive free region `'static` f(x) - //~^ ERROR free region `'_#1r` does not outlive free region `'static` } fn main() {} diff --git a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs index 701a7c6b056a6..39eafa1004026 100644 --- a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs +++ b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs @@ -17,8 +17,8 @@ fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 { // in `g`. These are related via the `UnsafeFnPointer` cast. let g: unsafe fn(_) -> _ = f; //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `'_#1r` does not outlive free region `'static` unsafe { g(input) } - //~^ ERROR free region `'_#1r` does not outlive free region `'static` } fn main() {} diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index f3a57c088409a..8feac15d69a82 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -27,17 +27,23 @@ fn main() { // END RUST SOURCE // START rustc.use_x.nll.0.mir // | Free Region Mapping -// | '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#3r] -// | '_#1r | External | ['_#1r] -// | '_#2r | External | ['_#2r, '_#1r] -// | '_#3r | Local | ['_#3r] +// | '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r] +// | '_#1r | External | ['_#1r, '_#4r] +// | '_#2r | External | ['_#2r, '_#1r, '_#4r] +// | '_#3r | Local | ['_#4r, '_#3r] +// | '_#4r | Local | ['_#4r] // | // | Inferred Region Values -// | '_#0r | {'_#0r, bb0[0], bb0[1]} -// | '_#1r | {'_#1r, bb0[0], bb0[1]} -// | '_#2r | {'_#2r, bb0[0], bb0[1]} -// | '_#3r | {'_#3r, bb0[0], bb0[1]} +// | '_#0r | {'_#0r, bb0[0..=1]} +// | '_#1r | {'_#1r, bb0[0..=1]} +// | '_#2r | {'_#2r, bb0[0..=1]} +// | '_#3r | {'_#3r, bb0[0..=1]} +// | '_#4r | {'_#4r, bb0[0..=1]} +// | '_#5r | {'_#1r, bb0[0..=1]} +// | '_#6r | {'_#2r, bb0[0..=1]} +// | '_#7r | {'_#1r, bb0[0..=1]} +// | '_#8r | {'_#3r, bb0[0..=1]} // | // ... -// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { +// fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r 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 d203472f20c73..f69c51c3562dc 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,11 +28,11 @@ 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]} +// | '_#7r | {bb0[6..=14]} // ... -// | '_#8r | {bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#9r | {bb0[11..=14]} // ... -// let _2: &'_#6r mut i32; +// let _2: &'_#7r mut i32; // ... -// let _4: &'_#8r mut i32; +// let _4: &'_#9r mut i32; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index c04cedbc04b4d..e9834305550c3 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,15 +31,15 @@ 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]} +// | '_#2r | {bb2[0..=1], bb3[0..=1]} +// | '_#3r | {bb2[1], bb3[0..=1]} // ... -// let _2: &'_#2r usize; +// let _2: &'_#3r usize; // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // bb2: { // | Live variables on entry to bb2[0]: [_1, _3] -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // | Live variables on entry to bb2[1]: [_2] // switchInt(const true) -> [0u8: bb4, otherwise: bb3]; // } 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 e2ad49a443625..c14ce6bb581de 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,7 @@ 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]} +// | '_#6r | {bb2[3..=5], bb3[0..=1]} +// ... +// let _2: Wrap<&'_#6r usize>; // 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 e0272a51d03d9..058a57fe612cf 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,7 @@ 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]} +// | '_#6r | {bb2[3..=5], bb3[0..=2], bb4[0], bb5[0..=2], bb6[0], bb7[0..=1], bb8[0]} +// ... +// let _2: Wrap<&'_#6r usize>; // 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 8aa0eb1a3a90e..821cd73667193 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,14 +36,14 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#1r | {bb2[0], bb2[1], bb3[0], bb3[1]} +// | '_#2r | {bb2[0..=1], bb3[0..=1]} // ... -// | '_#3r | {bb8[1], bb8[2], bb8[3], bb8[4]} -// | '_#4r | {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]} +// | '_#4r | {bb8[1..=4]} +// | '_#5r | {bb2[1], bb3[0..=1], bb8[2..=4]} // ... -// let mut _2: &'_#4r usize; +// let mut _2: &'_#5r usize; // ... -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // ... -// _2 = &'_#3r (*_10); +// _2 = &'_#4r (*_10); // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 2bc165bd3c4a3..a3f68ed5ebf5e 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,16 +32,16 @@ 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]} +// | '_#2r | {bb2[0..=6], bb3[0..=1]} +// | '_#3r | {bb2[1..=6], bb3[0..=1]} +// | '_#4r | {bb2[5..=6], bb3[0..=1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir -// let _2: &'_#2r usize; +// let _2: &'_#3r usize; // ... -// let _6: &'_#3r usize; +// let _6: &'_#4r usize; // ... -// _2 = &'_#1r _1[_3]; +// _2 = &'_#2r _1[_3]; // ... // _7 = _2; // ... diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr index 6b57f91987be5..9f62b2a41c5b9 100644 --- a/src/test/ui/nll/capture-ref-in-struct.stderr +++ b/src/test/ui/nll/capture-ref-in-struct.stderr @@ -7,7 +7,7 @@ error[E0597]: `y` does not live long enough 37 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#4r... + = note: borrowed value must be valid for lifetime '_#5r... error: aborting due to previous error 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 c842d51a2ad74..2dfafd8f1725b 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -10,7 +10,7 @@ error: free region `'_#4r` does not outlive free region `'_#3r` 36 | let mut closure = expect_sig(|p, y| *p = y); | ^^^^^^ -note: External requirements +note: No external requirements --> $DIR/escape-argument-callee.rs:36:38 | 36 | let mut closure = expect_sig(|p, y| *p = y); @@ -20,7 +20,6 @@ note: External requirements i16, for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) i32)) ] - = note: number of external vids: 1 note: No external requirements --> $DIR/escape-argument-callee.rs:30:1 diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 0ec671997e7af..2b0e3661376d3 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -1,4 +1,4 @@ -note: External requirements +note: No external requirements --> $DIR/escape-argument.rs:36:38 | 36 | let mut closure = expect_sig(|p, y| *p = y); @@ -8,7 +8,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) ] - = note: number of external vids: 1 note: No external requirements --> $DIR/escape-argument.rs:30:1 @@ -33,7 +32,7 @@ error[E0597]: `y` does not live long enough 39 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#5r... + = note: borrowed value must be valid for lifetime '_#6r... error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index 6c70afa0c9c8a..ddda72c5686ef 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -59,7 +59,7 @@ error[E0597]: `y` does not live long enough 36 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#3r... + = note: borrowed value must be valid for lifetime '_#4r... error: aborting due to previous error 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 0b982dd812b59..a0814cfc15fa4 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -36,7 +36,7 @@ error[E0597]: `y` does not live long enough 36 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#3r... + = note: borrowed value must be valid for lifetime '_#4r... 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 index c2f071cc029e6..50d7877de50d7 100644 --- 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 @@ -54,8 +54,8 @@ fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell // Only works if 'x: 'y: let p = x.get(); //~^ WARN not reporting region error due to -Znll + //~| ERROR free region `'_#5r` does not outlive free region `'_#6r` demand_y(x, y, p) - //~^ ERROR free region `'_#5r` does not outlive free region `'_#6r` }, ); } 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 d581622c4c63e..f90bc7c175a96 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 @@ -5,20 +5,20 @@ warning: not reporting region error due to -Znll | ^^^^^^^ error: free region `'_#5r` does not outlive free region `'_#6r` - --> $DIR/propagate-approximated-fail-no-postdom.rs:57:25 + --> $DIR/propagate-approximated-fail-no-postdom.rs:55:17 | -57 | demand_y(x, y, p) - | ^ +55 | let p = x.get(); + | ^ -note: External requirements +note: No 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` +57 | | //~| ERROR free region `'_#5r` does not outlive free region `'_#6r` +58 | | demand_y(x, y, p) 59 | | }, | |_________^ | @@ -26,7 +26,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>)) ] - = note: number of external vids: 4 note: No external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:48:1 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 717cf481a01d1..4bae29ad32617 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -24,10 +24,10 @@ note: External requirements = note: where '_#1r: '_#2r error: free region `'_#1r` does not outlive free region `'_#2r` - --> $DIR/propagate-approximated-ref.rs:53:38 + --> $DIR/propagate-approximated-ref.rs:53:29 | 53 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ^^^^^^^ + | ^^^^^^^ note: No external requirements --> $DIR/propagate-approximated-ref.rs:52:1 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 index b93c69dc13f46..c5107322f6f2c 100644 --- 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 @@ -10,7 +10,7 @@ error: free region `'_#2r` does not outlive free region `'_#1r` 33 | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure | ^^^^^^ -note: External requirements +note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15 | 31 | foo(cell, |cell_a, cell_x| { @@ -25,7 +25,6 @@ note: External requirements 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 @@ -80,7 +79,7 @@ error[E0597]: `a` does not live long enough 49 | } | - borrowed value only lives until here | - = note: borrowed value must be valid for lifetime '_#1r... + = note: borrowed value must be valid for lifetime '_#2r... error: aborting due to 2 previous errors 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..502b344c89e44 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-to-empty.stderr @@ -0,0 +1,45 @@ +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:18 + | +41 | demand_y(x, y, x.get()) + | ^ + +note: No 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: 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-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index 43464bfb2b9a7..43d61fdf1b5f8 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -24,10 +24,10 @@ note: External requirements = note: where '_#1r: '_#2r error: free region `'_#1r` does not outlive free region `'_#2r` - --> $DIR/propagate-approximated-val.rs:46:37 + --> $DIR/propagate-approximated-val.rs:46:29 | 46 | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { - | ^^^^^^ + | ^^^^^^ note: No external requirements --> $DIR/propagate-approximated-val.rs:45:1 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 index 6094f9aad81da..f3c40c838fb3e 100644 --- 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 @@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll | ^^^^^^^^^^^^^^^^^^^^^^^ error: free region `'_#6r` does not outlive free region `'_#4r` - --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:21 + --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:18 | 47 | demand_y(x, y, x.get()) - | ^ + | ^ -note: External requirements +note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47 | 45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { @@ -26,7 +26,6 @@ note: External requirements 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 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 index 6658ee63abd9d..a66c2a7897024 100644 --- 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 @@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll | ^^^^^^^^^^^^^^^^^^^^^^^ error: free region `'_#5r` does not outlive free region `'_#7r` - --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:21 + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:18 | 51 | demand_y(x, y, x.get()) - | ^ + | ^ -note: External requirements +note: No external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47 | 49 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { @@ -26,7 +26,6 @@ note: External requirements 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 diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs new file mode 100644 index 0000000000000..604c81da49db3 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -0,0 +1,60 @@ +// 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 that regions which appear only in the closure's generics (in +// this case, `'a`) are properly mapped to the creator's generics. In +// this case, the closure constrains its type parameter `T` to outlive +// the same `'a` for which it implements `Trait`, which can only be the `'a` +// from the function definition. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +trait Trait<'a> {} + +fn establish_relationships(value: T, closure: F) +where + F: FnOnce(T), +{ + closure(value) +} + +fn require<'a, T>(t: T) +where + T: Trait<'a> + 'a, +{ +} + +#[rustc_regions] +fn supply<'a, T>(value: T) +where + T: Trait<'a>, +{ + establish_relationships(value, |value| { + //~^ ERROR `T` does not outlive + + // This function call requires that + // + // (a) T: Trait<'a> + // + // and + // + // (b) T: 'a + // + // The latter does not hold. + + require(value); + //~^ WARNING not reporting region error due to -Znll + }); +} + +fn main() {} diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr new file mode 100644 index 0000000000000..efac55f2beeac --- /dev/null +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -0,0 +1,60 @@ +warning: not reporting region error due to -Znll + --> $DIR/propagate-from-trait-match.rs:55:9 + | +55 | require(value); + | ^^^^^^^ + +note: External requirements + --> $DIR/propagate-from-trait-match.rs:42:36 + | +42 | establish_relationships(value, |value| { + | ____________________________________^ +43 | | //~^ ERROR `T` does not outlive +44 | | +45 | | // This function call requires that +... | +56 | | //~^ WARNING not reporting region error due to -Znll +57 | | }); + | |_____^ + | + = note: defining type: DefId(0/1:16 ~ propagate_from_trait_match[317d]::supply[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((T,)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +error: `T` does not outlive `'_#3r` + --> $DIR/propagate-from-trait-match.rs:42:36 + | +42 | establish_relationships(value, |value| { + | ____________________________________^ +43 | | //~^ ERROR `T` does not outlive +44 | | +45 | | // This function call requires that +... | +56 | | //~^ WARNING not reporting region error due to -Znll +57 | | }); + | |_____^ + +note: No external requirements + --> $DIR/propagate-from-trait-match.rs:38:1 + | +38 | / fn supply<'a, T>(value: T) +39 | | where +40 | | T: Trait<'a>, +41 | | { +... | +57 | | }); +58 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ propagate_from_trait_match[317d]::supply[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to previous error + 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 8999f69e8ded6..cb2b2e2f11860 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 @@ -10,7 +10,7 @@ error: free region `'_#3r` does not outlive free region `'_#2r` 21 | expect_sig(|a, b| b); // ought to return `a` | ^ -note: External requirements +note: No external requirements --> $DIR/return-wrong-bound-region.rs:21:16 | 21 | expect_sig(|a, b| b); // ought to return `a` @@ -20,7 +20,6 @@ note: External requirements i16, for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) -> &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32 ] - = note: number of external vids: 1 note: No external requirements --> $DIR/return-wrong-bound-region.rs:20:1 diff --git a/src/test/ui/nll/projection-return.rs b/src/test/ui/nll/projection-return.rs new file mode 100644 index 0000000000000..31388cf50c558 --- /dev/null +++ b/src/test/ui/nll/projection-return.rs @@ -0,0 +1,29 @@ +// 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 +// must-compile-successfully + +#![feature(rustc_attrs)] + +trait Foo { + type Bar; +} + +impl Foo for () { + type Bar = u32; +} + +fn foo() -> <() as Foo>::Bar { + 22 +} + +fn main() { } + diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs new file mode 100644 index 0000000000000..9451163ace993 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs @@ -0,0 +1,68 @@ +// 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 -Zverbose + +// Tests closures that propagate an outlives relationship to their +// creator where the subject is a projection with no regions (`::Item`, to be exact). + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +trait Anything { } + +impl Anything for T { } + +fn with_signature<'a, T, F>(x: Box, op: F) -> Box + where F: FnOnce(Box) -> Box +{ + op(x) +} + +#[rustc_regions] +fn no_region<'a, T>(x: Box) -> Box +where + T: Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `::Item` does not outlive +} + +#[rustc_regions] +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) +} + +#[rustc_regions] +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Iterator, +{ + with_signature(x, |mut y| Box::new(y.next())) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `::Item` does not outlive +} + +#[rustc_regions] +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Iterator, + 'b: 'a, +{ + with_signature(x, |mut y| Box::new(y.next())) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr new file mode 100644 index 0000000000000..9afd5d41182f4 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -0,0 +1,157 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-closure.rs:36:31 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-closure.rs:54:31 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^ + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:36:23 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:15 ~ projection_no_regions_closure[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where ::Item: '_#2r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:46:23 + | +46 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:18 ~ projection_no_regions_closure[317d]::correct_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where ::Item: '_#2r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:54:23 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:22 ~ projection_no_regions_closure[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 4 + = note: where ::Item: '_#3r + +note: External requirements + --> $DIR/projection-no-regions-closure.rs:65:23 + | +65 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:26 ~ projection_no_regions_closure[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 4 + = note: where ::Item: '_#3r + +error: `::Item` does not outlive `'_#4r` + --> $DIR/projection-no-regions-closure.rs:36:23 + | +36 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:32:1 + | +32 | / fn no_region<'a, T>(x: Box) -> Box +33 | | where +34 | | T: Iterator, +35 | | { +... | +38 | | //~| ERROR `::Item` does not outlive +39 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ projection_no_regions_closure[317d]::no_region[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:42:1 + | +42 | / fn correct_region<'a, T>(x: Box) -> Box +43 | | where +44 | | T: 'a + Iterator, +45 | | { +46 | | with_signature(x, |mut y| Box::new(y.next())) +47 | | } + | |_^ + | + = note: defining type: DefId(0/0:7 ~ projection_no_regions_closure[317d]::correct_region[0]) with substs [ + '_#1r, + T + ] + +error: `::Item` does not outlive `'_#6r` + --> $DIR/projection-no-regions-closure.rs:54:23 + | +54 | with_signature(x, |mut y| Box::new(y.next())) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:50:1 + | +50 | / fn wrong_region<'a, 'b, T>(x: Box) -> Box +51 | | where +52 | | T: 'b + Iterator, +53 | | { +... | +56 | | //~| ERROR `::Item` does not outlive +57 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_no_regions_closure[317d]::wrong_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-no-regions-closure.rs:60:1 + | +60 | / fn outlives_region<'a, 'b, T>(x: Box) -> Box +61 | | where +62 | | T: 'b + Iterator, +63 | | 'b: 'a, +64 | | { +65 | | with_signature(x, |mut y| Box::new(y.next())) +66 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_no_regions_closure[317d]::outlives_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs new file mode 100644 index 0000000000000..c815fdc1a0c5e --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.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. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] + +trait Anything { } + +impl Anything for T { } + +fn no_region<'a, T>(mut x: T) -> Box +where + T: Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `::Item` does not outlive +} + +fn correct_region<'a, T>(mut x: T) -> Box +where + T: 'a + Iterator, +{ + Box::new(x.next()) +} + +fn wrong_region<'a, 'b, T>(mut x: T) -> Box +where + T: 'b + Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `::Item` does not outlive +} + +fn outlives_region<'a, 'b, T>(mut x: T) -> Box +where + T: 'b + Iterator, + 'b: 'a, +{ + Box::new(x.next()) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr new file mode 100644 index 0000000000000..4d13972641c16 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-no-regions-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: `::Item` does not outlive `'_#4r` + --> $DIR/projection-no-regions-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: `::Item` does not outlive `'_#5r` + --> $DIR/projection-no-regions-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs new file mode 100644 index 0000000000000..e2a2d20d77d5c --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -0,0 +1,106 @@ +// 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 cases where we constrain `>::AssocType` to +// outlive `'a` and there are no bounds in the trait definition of +// `Anything`. This means that the constraint can only be satisfied in two +// ways: +// +// - by ensuring that `T: 'a` and `'b: 'a`, or +// - by something in the where clauses. +// +// As of this writing, the where clause option does not work because +// of limitations in our region inferencing system (this is true both +// with and without NLL). See `projection_outlives`. +// +// Ensuring that both `T: 'a` and `'b: 'a` holds does work (`elements_outlive`). + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `T` does not outlive + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `T` does not outlive + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `T` does not outlive + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T: 'a, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr new file mode 100644 index 0000000000000..cbd80d70bf955 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -0,0 +1,194 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:56:39 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:68:39 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-closure.rs:90:39 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-one-region-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + = note: where '_#1r: '_#2r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:68:29 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:90:29 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-closure.rs:103:29 + | +103 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + = note: where '_#2r: '_#3r + +error: `T` does not outlive `'_#5r` + --> $DIR/projection-one-region-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-one-region-closure.rs:56:20 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:52:1 + | +52 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +53 | | where +54 | | T: Anything<'b>, +55 | | { +... | +59 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +60 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +error: `T` does not outlive `'_#6r` + --> $DIR/projection-one-region-closure.rs:68:29 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-closure.rs:68:20 + | +68 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:63:1 + | +63 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +64 | | where +65 | | T: Anything<'b>, +66 | | 'a: 'a, +... | +71 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +72 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: `T` does not outlive `'_#6r` + --> $DIR/projection-one-region-closure.rs:90:29 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-closure.rs:90:20 + | +90 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:75:1 + | +75 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +76 | | where +77 | | T: Anything<'b>, +78 | | T::AssocType: 'a, +... | +93 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +94 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-closure.rs:97:1 + | +97 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +98 | | where +99 | | T: Anything<'b>, +100 | | T: 'a, +... | +103 | | with_signature(cell, t, |cell, t| require(cell, t)); +104 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs new file mode 100644 index 0000000000000..e179927dfb0b9 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs @@ -0,0 +1,106 @@ +// 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 cases where we constrain `>::AssocType` to +// outlive `'a` and there is a unique bound in the trait definition of +// `Anything` -- i.e., we know that `AssocType` outlives `'b`. In this +// case, the best way to satisfy the trait bound is to show that `'b: +// 'a`, which can be done in various ways. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType: 'a; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr new file mode 100644 index 0000000000000..1088ae846fee5 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -0,0 +1,204 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:48:39 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:59:39 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-one-region-trait-bound-closure.rs:80:39 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:48:29 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where '_#1r: '_#2r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:59:29 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:80:29 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:91:29 + | +91 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where '_#2r: '_#3r + +note: External requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:103:29 + | +103 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:34 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where '_#1r: '_#2r + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-one-region-trait-bound-closure.rs:48:20 + | +48 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:44:1 + | +44 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +45 | | where +46 | | T: Anything<'b>, +47 | | { +... | +50 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +51 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-trait-bound-closure.rs:59:20 + | +59 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:54:1 + | +54 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +55 | | where +56 | | T: Anything<'b>, +57 | | 'a: 'a, +... | +61 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +62 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/projection-one-region-trait-bound-closure.rs:80:20 + | +80 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:65:1 + | +65 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +66 | | where +67 | | T: Anything<'b>, +68 | | T::AssocType: 'a, +... | +82 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +83 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:86:1 + | +86 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +87 | | where +88 | | T: Anything<'b>, +89 | | 'b: 'a, +90 | | { +91 | | with_signature(cell, t, |cell, t| require(cell, t)); +92 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-closure.rs:95:1 + | +95 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +96 | | where +97 | | T: Anything<'a>, +98 | | { +... | +103 | | with_signature(cell, t, |cell, t| require(cell, t)); +104 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs new file mode 100644 index 0000000000000..67e28af11469d --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.rs @@ -0,0 +1,99 @@ +// 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 cases where we constrain `>::AssocType` to +// outlive `'static`. In this case, we don't get any errors, and in fact +// we don't even propagate constraints from the closures to the callers. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose +// must-compile-successfully + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a> { + type AssocType: 'static; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr new file mode 100644 index 0000000000000..986676d28d920 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr @@ -0,0 +1,155 @@ +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:47:29 + | +47 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:19 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:56:29 + | +56 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:23 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:75:29 + | +75 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:84:29 + | +84 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:31 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:96:29 + | +96 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:34 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:43:1 + | +43 | / fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +44 | | where +45 | | T: Anything<'b>, +46 | | { +47 | | with_signature(cell, t, |cell, t| require(cell, t)); +48 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:51:1 + | +51 | / fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +52 | | where +53 | | T: Anything<'b>, +54 | | 'a: 'a, +55 | | { +56 | | with_signature(cell, t, |cell, t| require(cell, t)); +57 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_one_region_trait_bound_static_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:60:1 + | +60 | / fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +61 | | where +62 | | T: Anything<'b>, +63 | | T::AssocType: 'a, +... | +75 | | with_signature(cell, t, |cell, t| require(cell, t)); +76 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_one_region_trait_bound_static_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:79:1 + | +79 | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +80 | | where +81 | | T: Anything<'b>, +82 | | 'b: 'a, +83 | | { +84 | | with_signature(cell, t, |cell, t| require(cell, t)); +85 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_one_region_trait_bound_static_closure[317d]::elements_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-one-region-trait-bound-static-closure.rs:88:1 + | +88 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +89 | | where +90 | | T: Anything<'a>, +91 | | { +... | +96 | | with_signature(cell, t, |cell, t| require(cell, t)); +97 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_one_region_trait_bound_static_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs new file mode 100644 index 0000000000000..42bfdfcf9f91b --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -0,0 +1,135 @@ +// 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 cases where we constrain `>::AssocType` +// to outlive `'a` and there are two bounds in the trait definition of +// `Anything` -- i.e., we know that `AssocType` outlives `'a` and +// `'b`. In this case, it's not clear what is the best way to satisfy +// the trait bound, and hence we propagate it to the caller as a type +// test. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +trait Anything<'a, 'b> { + type AssocType: 'a + 'b; +} + +fn with_signature<'a, T, F>(cell: Cell<&'a ()>, t: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(cell, t) +} + +fn require<'a, 'b, 'c, T>(_cell: Cell<&'a ()>, _t: T) +where + T: Anything<'b, 'c>, + T::AssocType: 'a, +{ +} + +#[rustc_regions] +fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `>::AssocType` does not outlive `'_#7r` +} + +#[rustc_regions] +fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'a: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `>::AssocType` does not outlive `'_#8r` +} + +#[rustc_regions] +fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + T::AssocType: 'a, +{ + // This error is unfortunate. This code ought to type-check: we + // are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. However, + // the way the region checker works, we don't register this + // outlives obligation, and hence we get an error: this is because + // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will + // equal `'b` or not, so we ignore the where-clause. Obviously we + // can do better here with a more involved verification step. + + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `>::AssocType` does not outlive `'_#8r` +} + +#[rustc_regions] +fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'c>, + 'c: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'b>, +{ + with_signature(cell, t, |cell, t| require(cell, t)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +} + +#[rustc_regions] +fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'b, 'b>, + 'b: 'a, +{ + with_signature(cell, t, |cell, t| require(cell, t)); +} + +#[rustc_regions] +fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +where + T: Anything<'a, 'a>, +{ + // Note that in this case the closure still propagates an external + // requirement between two variables in its signature, but the + // creator maps both those two region variables to `'a` on its + // side. + with_signature(cell, t, |cell, t| require(cell, t)); +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr new file mode 100644 index 0000000000000..5b708a0d7e629 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -0,0 +1,326 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:49:39 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:60:39 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:81:39 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-two-region-trait-bound-closure.rs:109:39 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^ + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:22 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where >::AssocType: '_#3r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:27 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_early[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:32 ~ projection_two_region_trait_bound_closure[317d]::projection_outlives[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:92:29 + | +92 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:37 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive1[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:101:29 + | +101 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:42 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive2[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + '_#3r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)) + ] + = note: number of external vids: 5 + = note: where >::AssocType: '_#4r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:109:29 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:46 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where >::AssocType: '_#2r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:120:29 + | +120 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:50 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where >::AssocType: '_#3r + +note: External requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:132:29 + | +132 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:53 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where >::AssocType: '_#2r + +error: `>::AssocType` does not outlive `'_#7r` + --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 + | +49 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:45:1 + | +45 | / fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +46 | | where +47 | | T: Anything<'b, 'c>, +48 | | { +... | +51 | | //~| ERROR `>::AssocType` does not outlive `'_#7r` +52 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: `>::AssocType` does not outlive `'_#8r` + --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 + | +60 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:55:1 + | +55 | / fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +56 | | where +57 | | T: Anything<'b, 'c>, +58 | | 'a: 'a, +... | +62 | | //~| ERROR `>::AssocType` does not outlive `'_#8r` +63 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_early[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +error: `>::AssocType` does not outlive `'_#8r` + --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 + | +81 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:66:1 + | +66 | / fn projection_outlives<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +67 | | where +68 | | T: Anything<'b, 'c>, +69 | | T::AssocType: 'a, +... | +83 | | //~| ERROR `>::AssocType` does not outlive `'_#8r` +84 | | } + | |_^ + | + = note: defining type: DefId(0/0:10 ~ projection_two_region_trait_bound_closure[317d]::projection_outlives[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:87:1 + | +87 | / fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +88 | | where +89 | | T: Anything<'b, 'c>, +90 | | 'b: 'a, +91 | | { +92 | | with_signature(cell, t, |cell, t| require(cell, t)); +93 | | } + | |_^ + | + = note: defining type: DefId(0/0:11 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive1[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:96:1 + | +96 | / fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) +97 | | where +98 | | T: Anything<'b, 'c>, +99 | | 'c: 'a, +100 | | { +101 | | with_signature(cell, t, |cell, t| require(cell, t)); +102 | | } + | |_^ + | + = note: defining type: DefId(0/0:12 ~ projection_two_region_trait_bound_closure[317d]::elements_outlive2[0]) with substs [ + '_#1r, + '_#2r, + '_#3r, + T + ] + +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + --> $DIR/projection-two-region-trait-bound-closure.rs:109:20 + | +109 | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^ + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:105:1 + | +105 | / fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +106 | | where +107 | | T: Anything<'b, 'b>, +108 | | { +... | +111 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +112 | | } + | |_^ + | + = note: defining type: DefId(0/0:13 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:115:1 + | +115 | / fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) +116 | | where +117 | | T: Anything<'b, 'b>, +118 | | 'b: 'a, +119 | | { +120 | | with_signature(cell, t, |cell, t| require(cell, t)); +121 | | } + | |_^ + | + = note: defining type: DefId(0/0:14 ~ projection_two_region_trait_bound_closure[317d]::two_regions_outlive[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +note: No external requirements + --> $DIR/projection-two-region-trait-bound-closure.rs:124:1 + | +124 | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) +125 | | where +126 | | T: Anything<'a, 'a>, +127 | | { +... | +132 | | with_signature(cell, t, |cell, t| require(cell, t)); +133 | | } + | |_^ + | + = note: defining type: DefId(0/0:15 ~ projection_two_region_trait_bound_closure[317d]::one_region[0]) with substs [ + '_#1r, + T + ] + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs new file mode 100644 index 0000000000000..14e2eb26976ab --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs @@ -0,0 +1,66 @@ +// 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 -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::fmt::Debug; + +fn with_signature<'a, T, F>(x: Box, op: F) -> Box + where F: FnOnce(Box) -> Box +{ + op(x) +} + +#[rustc_regions] +fn no_region<'a, T>(x: Box) -> Box +where + T: Debug, +{ + // Here, the closure winds up being required to prove that `T: + // 'a`. In principle, it could know that, except that it is + // type-checked in a fully generic way, and hence it winds up with + // a propagated requirement that `T: '_#2`, where `'_#2` appears + // in the return type. The caller makes the mapping from `'_#2` to + // `'a` (and subsequently reports an error). + + with_signature(x, |y| y) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `T` does not outlive +} + +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `T` does not outlive +} + +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr new file mode 100644 index 0000000000000..37ebc38da4dae --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -0,0 +1,58 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:27 + | +37 | with_signature(x, |y| y) + | ^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 + | +53 | x + | ^ + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 + | +37 | with_signature(x, |y| y) + | ^^^^^ + | + = note: defining type: DefId(0/1:14 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +error: `T` does not outlive `'_#4r` + --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 + | +37 | with_signature(x, |y| y) + | ^^^^^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-return-type.rs:26:1 + | +26 | / fn no_region<'a, T>(x: Box) -> Box +27 | | where +28 | | T: Debug, +29 | | { +... | +39 | | //~| ERROR `T` does not outlive +40 | | } + | |_^ + | + = note: defining type: DefId(0/0:5 ~ ty_param_closure_outlives_from_return_type[317d]::no_region[0]) with substs [ + '_#1r, + T + ] + +error: `T` does not outlive `'_#4r` + --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 + | +53 | x + | ^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs new file mode 100644 index 0000000000000..beed1a740eac3 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs @@ -0,0 +1,96 @@ +// 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 that we can propagate `T: 'a` obligations to our caller. See +// `correct_region` for an explanation of how this test is setup; it's +// somewhat intricate. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn with_signature<'a, T, F>(a: Cell<&'a ()>, b: T, op: F) +where + F: FnOnce(Cell<&'a ()>, T), +{ + op(a, b) +} + +fn require<'a, T>(_a: &Cell<&'a ()>, _b: &T) +where + T: 'a, +{ +} + +#[rustc_regions] +fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { + with_signature(a, b, |x, y| { + //~^ ERROR `T` does not outlive + // + // See `correct_region`, which explains the point of this + // test. The only difference is that, in the case of this + // function, there is no where clause *anywhere*, and hence we + // get an error (but reported by the closure creator). + require(&x, &y) + //~^ WARNING not reporting region error due to -Znll + }) +} + +#[rustc_regions] +fn correct_region<'a, T>(a: Cell<&'a ()>, b: T) +where + T: 'a, +{ + with_signature(a, b, |x, y| { + // Key point of this test: + // + // The *closure* is being type-checked with all of its free + // regions "universalized". In particular, it does not know + // that `x` has the type `Cell<&'a ()>`, but rather treats it + // as if the type of `x` is `Cell<&'A ()>`, where `'A` is some + // fresh, independent region distinct from the `'a` which + // appears in the environment. The call to `require` here + // forces us then to prove that `T: 'A`, but the closure + // cannot do it on its own. It has to surface this requirement + // to its creator (which knows that `'a == 'A`). + require(&x, &y) + }) +} + +#[rustc_regions] +fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +where + T: 'b, +{ + with_signature(a, b, |x, y| { + //~^ ERROR `T` does not outlive + // See `correct_region` + require(&x, &y) + //~^ WARNING not reporting region error due to -Znll + }) +} + +#[rustc_regions] +fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +where + T: 'b, + 'b: 'a, +{ + with_signature(a, b, |x, y| { + // See `correct_region` + require(&x, &y) + }) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr new file mode 100644 index 0000000000000..78445eb47c328 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -0,0 +1,191 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:45:9 + | +45 | require(&x, &y) + | ^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:79:9 + | +79 | require(&x, &y) + | ^^^^^^^ + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 + | +38 | with_signature(a, b, |x, y| { + | __________________________^ +39 | | //~^ ERROR `T` does not outlive +40 | | // +41 | | // See `correct_region`, which explains the point of this +... | +46 | | //~^ WARNING not reporting region error due to -Znll +47 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:16 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]::{{closure}}[0]) with closure substs [ + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:55:26 + | +55 | with_signature(a, b, |x, y| { + | __________________________^ +56 | | // Key point of this test: +57 | | // +58 | | // The *closure* is being type-checked with all of its free +... | +67 | | require(&x, &y) +68 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:19 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 + | +76 | with_signature(a, b, |x, y| { + | __________________________^ +77 | | //~^ ERROR `T` does not outlive +78 | | // See `correct_region` +79 | | require(&x, &y) +80 | | //~^ WARNING not reporting region error due to -Znll +81 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:23 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)) + ] + = note: number of external vids: 3 + = note: where T: '_#2r + +note: External requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:90:26 + | +90 | with_signature(a, b, |x, y| { + | __________________________^ +91 | | // See `correct_region` +92 | | require(&x, &y) +93 | | }) + | |_____^ + | + = note: defining type: DefId(0/1:27 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]::{{closure}}[0]) with closure substs [ + '_#1r, + '_#2r, + T, + i32, + extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) + ] + = note: number of external vids: 4 + = note: where T: '_#3r + +error: `T` does not outlive `'_#3r` + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 + | +38 | with_signature(a, b, |x, y| { + | __________________________^ +39 | | //~^ ERROR `T` does not outlive +40 | | // +41 | | // See `correct_region`, which explains the point of this +... | +46 | | //~^ WARNING not reporting region error due to -Znll +47 | | }) + | |_____^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:37:1 + | +37 | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { +38 | | with_signature(a, b, |x, y| { +39 | | //~^ ERROR `T` does not outlive +40 | | // +... | +47 | | }) +48 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]) with substs [ + T + ] + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:51:1 + | +51 | / fn correct_region<'a, T>(a: Cell<&'a ()>, b: T) +52 | | where +53 | | T: 'a, +54 | | { +... | +68 | | }) +69 | | } + | |_^ + | + = note: defining type: DefId(0/0:7 ~ ty_param_closure_outlives_from_where_clause[317d]::correct_region[0]) with substs [ + '_#1r, + T + ] + +error: `T` does not outlive `'_#5r` + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 + | +76 | with_signature(a, b, |x, y| { + | __________________________^ +77 | | //~^ ERROR `T` does not outlive +78 | | // See `correct_region` +79 | | require(&x, &y) +80 | | //~^ WARNING not reporting region error due to -Znll +81 | | }) + | |_____^ + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:72:1 + | +72 | / fn wrong_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +73 | | where +74 | | T: 'b, +75 | | { +... | +81 | | }) +82 | | } + | |_^ + | + = note: defining type: DefId(0/0:8 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]) with substs [ + '_#1r, + T + ] + +note: No external requirements + --> $DIR/ty-param-closure-outlives-from-where-clause.rs:85:1 + | +85 | / fn outlives_region<'a, 'b, T>(a: Cell<&'a ()>, b: T) +86 | | where +87 | | T: 'b, +88 | | 'b: 'a, +... | +93 | | }) +94 | | } + | |_^ + | + = note: defining type: DefId(0/0:9 ~ ty_param_closure_outlives_from_where_clause[317d]::outlives_region[0]) with substs [ + '_#1r, + '_#2r, + T + ] + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs new file mode 100644 index 0000000000000..a1e636cbc444b --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs @@ -0,0 +1,41 @@ +// 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 we assume that universal types like `T` outlive the +// function body. + +#![allow(warnings)] +#![feature(dyn_trait)] + +use std::cell::Cell; + +// No errors here, because `'a` is local to the body. +fn region_within_body(t: T) { + let some_int = 22; + let cell = Cell::new(&some_int); + outlives(cell, t) +} + +// Error here, because T: 'a is not satisfied. +fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { + outlives(cell, t) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `T` does not outlive +} + +fn outlives<'a, T>(x: Cell<&'a usize>, y: T) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr new file mode 100644 index 0000000000000..bbe55c52b6ed5 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr @@ -0,0 +1,14 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn-body.rs:30:5 + | +30 | outlives(cell, t) + | ^^^^^^^^ + +error: `T` does not outlive `'_#4r` + --> $DIR/ty-param-fn-body.rs:30:5 + | +30 | outlives(cell, t) + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.rs b/src/test/ui/nll/ty-outlives/ty-param-fn.rs new file mode 100644 index 0000000000000..76783af4ceb0e --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.rs @@ -0,0 +1,51 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir + +#![allow(warnings)] +#![feature(dyn_trait)] + +use std::fmt::Debug; + +fn no_region<'a, T>(x: Box) -> Box +where + T: Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `T` does not outlive +} + +fn correct_region<'a, T>(x: Box) -> Box +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, +{ + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR `T` does not outlive +} + +fn outlives_region<'a, 'b, T>(x: Box) -> Box +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr new file mode 100644 index 0000000000000..02c4ebbd5aca9 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn.rs:22:5 + | +22 | x + | ^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-fn.rs:38:5 + | +38 | x + | ^ + +error: `T` does not outlive `'_#3r` + --> $DIR/ty-param-fn.rs:22:5 + | +22 | x + | ^ + +error: `T` does not outlive `'_#4r` + --> $DIR/ty-param-fn.rs:38:5 + | +38 | x + | ^ + +error: aborting due to 2 previous errors +