From 47c1921b9adb7c6e783ef4ec2a5f1dbe312b3da7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 13:14:36 -0500 Subject: [PATCH] move some parts of liveness to happen during type checking This allows us to re-use the `normalize` method on `TypeCheck`, which is important since normalization may create fresh region variables. This is not an ideal solution, though, since the current representation of "liveness constraints" (a vector of (region, point) pairs) is rather inefficient. Could do somewhat better by converting to indices, but it'd still be less good than the older code. Unclear how important this is. --- .../borrow_check/nll/constraint_generation.rs | 173 +------------- src/librustc_mir/borrow_check/nll/mod.rs | 23 +- .../borrow_check/nll/region_infer/mod.rs | 18 +- .../borrow_check/nll/type_check/liveness.rs | 220 ++++++++++++++++++ .../borrow_check/nll/type_check/mod.rs | 37 ++- src/test/ui/nll/ty-outlives/projection-fn.rs | 53 +++++ .../ui/nll/ty-outlives/projection-fn.stderr | 26 +++ 7 files changed, 351 insertions(+), 199 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/type_check/liveness.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-fn.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-fn.stderr 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>, - 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 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 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/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>, + 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 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)] + +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 failed type test +} + +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 failed type test +} + +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-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 +