diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 5564a0d98af..673e85e6b61 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 dataflow::{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<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>, - 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<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>, - 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<Local>)> = 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 c616f027331..3329448e2b7 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -77,7 +77,16 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( ) { // 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 constraint_sets = &type_check::type_check( + infcx, + mir_node_id, + param_env, + mir, + &liveness, + flow_inits, + move_data, + ); // Create the region inference context, taking ownership of the region inference // data that was contained in `infcx`. @@ -85,19 +94,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::compute(mir); // 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); 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 86ba7524bf0..f36392c9fcf 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -425,11 +425,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// 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. - fn check_type_tests( - &self, - infcx: &InferCtxt<'_, '_, 'tcx>, - mir: &Mir<'tcx>, - ) { + fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); @@ -473,13 +469,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { .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::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)), + RegionTest::All(tests) => tests.iter().all(|test| { + self.eval_region_test(mir, point, lower_bound, test) + }), } } 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 00000000000..e41bf7cda8e --- /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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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<MaybeInitializedLvals<'_, 'gcx, 'tcx>>, + 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<MaybeInitializedLvals<'flow, 'gcx, 'tcx>>, + 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<Local>)> = 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<T>(&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/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index fae911780a1..8f18105600a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.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,22 +29,43 @@ 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>, +pub(crate) fn type_check<'gcx, 'tcx>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + liveness: &LivenessResults, + flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>, + move_data: &MoveData<'tcx>, +) -> MirTypeckRegionConstraints<'tcx> { + type_check_internal(infcx, body_id, param_env, mir, &mut |cx| { + liveness::generate(cx, mir, liveness, flow_inits, move_data) + }) +} + +fn type_check_internal<'gcx, 'tcx>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + mir: &Mir<'tcx>, + extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), ) -> MirTypeckRegionConstraints<'tcx> { let mut checker = TypeChecker::new(infcx, body_id, param_env); let errors_reported = { @@ -55,9 +79,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,7 +530,7 @@ 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, @@ -1474,7 +1501,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, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/test/ui/nll/ty-outlives/projection-fn.rs b/src/test/ui/nll/ty-outlives/projection-fn.rs new file mode 100644 index 00000000000..677d1352788 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir + +#![allow(warnings)] +#![feature(dyn_trait)] + +trait Anything { } + +impl<T> Anything for T { } + +fn no_region<'a, T>(mut x: T) -> Box<dyn Anything + 'a> +where + T: Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn correct_region<'a, T>(mut x: T) -> Box<dyn Anything + 'a> +where + T: 'a + Iterator, +{ + Box::new(x.next()) +} + +fn wrong_region<'a, 'b, T>(mut x: T) -> Box<dyn Anything + 'a> +where + T: 'b + Iterator, +{ + Box::new(x.next()) + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn outlives_region<'a, 'b, T>(mut x: T) -> Box<dyn Anything + 'a> +where + T: 'b + Iterator, + 'b: 'a, +{ + Box::new(x.next()) +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-fn.stderr b/src/test/ui/nll/ty-outlives/projection-fn.stderr new file mode 100644 index 00000000000..a478348edc1 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-fn.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/projection-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#3r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn([]) } + --> $DIR/projection-fn.rs:24:5 + | +24 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/projection-fn.rs:40:5 + | +40 | Box::new(x.next()) + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +