From 93afb1affc9f4e7616e05a3fc2fdb66e81f35d1e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 10 Dec 2017 10:23:45 -0500 Subject: [PATCH] connect NLL type checker to the impl trait code We now add the suitable `impl Trait` constraints. --- src/librustc/infer/anon_types/mod.rs | 47 +++++++- src/librustc/util/ppaux.rs | 18 +-- src/librustc_mir/borrow_check/nll/mod.rs | 3 +- .../borrow_check/nll/region_infer/mod.rs | 4 +- src/librustc_mir/borrow_check/nll/renumber.rs | 34 ++++-- .../nll/type_check/input_output.rs | 109 +++++++++++++++++- .../borrow_check/nll/type_check/mod.rs | 8 +- .../borrow_check/nll/universal_regions.rs | 17 ++- src/librustc_typeck/check/mod.rs | 16 ++- .../run-pass/impl-trait/example-calendar.rs | 3 + .../ui/nll/ty-outlives/impl-trait-captures.rs | 27 +++++ .../ty-outlives/impl-trait-captures.stderr | 14 +++ .../ui/nll/ty-outlives/impl-trait-outlives.rs | 51 ++++++++ .../ty-outlives/impl-trait-outlives.stderr | 26 +++++ 14 files changed, 339 insertions(+), 38 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/impl-trait-captures.rs create mode 100644 src/test/ui/nll/ty-outlives/impl-trait-captures.stderr create mode 100644 src/test/ui/nll/ty-outlives/impl-trait-outlives.rs create mode 100644 src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr diff --git a/src/librustc/infer/anon_types/mod.rs b/src/librustc/infer/anon_types/mod.rs index f163076218a..a3d236ac1b6 100644 --- a/src/librustc/infer/anon_types/mod.rs +++ b/src/librustc/infer/anon_types/mod.rs @@ -93,20 +93,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// Moreover, it returns a `AnonTypeMap` that would map `?0` to /// info about the `impl Iterator<..>` type and `?1` to info about /// the `impl Debug` type. + /// + /// # Parameters + /// + /// - `parent_def_id` -- we will only instantiate anonymous types + /// with this parent. This is typically the def-id of the function + /// in whose return type anon types are being instantiated. + /// - `body_id` -- the body-id with which the resulting obligations should + /// be associated + /// - `param_env` -- the in-scope parameter environment to be used for + /// obligations + /// - `value` -- the value within which we are instantiating anon types pub fn instantiate_anon_types>( &self, + parent_def_id: DefId, body_id: ast::NodeId, param_env: ty::ParamEnv<'tcx>, value: &T, ) -> InferOk<'tcx, (T, AnonTypeMap<'tcx>)> { debug!( - "instantiate_anon_types(value={:?}, body_id={:?}, param_env={:?})", + "instantiate_anon_types(value={:?}, parent_def_id={:?}, body_id={:?}, param_env={:?})", value, + parent_def_id, body_id, param_env, ); let mut instantiator = Instantiator { infcx: self, + parent_def_id, body_id, param_env, anon_types: DefIdMap(), @@ -480,6 +494,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + parent_def_id: DefId, body_id: ast::NodeId, param_env: ty::ParamEnv<'tcx>, anon_types: AnonTypeMap<'tcx>, @@ -489,11 +504,33 @@ struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> { impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { fn instantiate_anon_types_in_map>(&mut self, value: &T) -> T { debug!("instantiate_anon_types_in_map(value={:?})", value); + let tcx = self.infcx.tcx; value.fold_with(&mut BottomUpFolder { - tcx: self.infcx.tcx, - fldop: |ty| if let ty::TyAnon(def_id, substs) = ty.sty { - self.fold_anon_ty(ty, def_id, substs) - } else { + tcx, + fldop: |ty| { + if let ty::TyAnon(def_id, substs) = ty.sty { + // Check that this is `impl Trait` type is declared by + // `parent_def_id`. During the first phase of type-check, this + // is true, but during NLL type-check, we sometimes encounter + // `impl Trait` types in e.g. inferred closure signatures that + // are not 'local' to the current function and hence which + // ought not to be instantiated. + if let Some(anon_node_id) = tcx.hir.as_local_node_id(def_id) { + let anon_parent_node_id = tcx.hir.get_parent(anon_node_id); + let anon_parent_def_id = tcx.hir.local_def_id(anon_parent_node_id); + if self.parent_def_id == anon_parent_def_id { + return self.fold_anon_ty(ty, def_id, substs); + } + + debug!("instantiate_anon_types_in_map: \ + encountered anon with wrong parent \ + def_id={:?} \ + anon_parent_def_id={:?}", + def_id, + anon_parent_def_id); + } + } + ty }, }) diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 5bfa6464568..51841836698 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -865,13 +865,17 @@ impl fmt::Debug for ty::RegionVid { define_print! { () ty::InferTy, (self, f, cx) { display { - match *self { - ty::TyVar(_) => write!(f, "_"), - ty::IntVar(_) => write!(f, "{}", "{integer}"), - ty::FloatVar(_) => write!(f, "{}", "{float}"), - ty::FreshTy(v) => write!(f, "FreshTy({})", v), - ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v), - ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v) + if cx.is_verbose { + print!(f, cx, print_debug(self)) + } else { + match *self { + ty::TyVar(_) => write!(f, "_"), + ty::IntVar(_) => write!(f, "{}", "{integer}"), + ty::FloatVar(_) => write!(f, "{}", "{float}"), + ty::FreshTy(v) => write!(f, "FreshTy({})", v), + ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v), + ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v) + } } } debug { diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 7acbe2ebf93..98a74f06703 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -77,13 +77,12 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( Option>, ) { // Run the MIR type-checker. - let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); let liveness = &LivenessResults::compute(mir); let constraint_sets = &type_check::type_check( infcx, - mir_node_id, param_env, mir, + def_id, &universal_regions, &liveness, flow_inits, 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 9f7219b1f55..6f3e6cb1ec5 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -246,13 +246,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { .map(|origin| RegionDefinition::new(origin)) .collect(); + let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.nll_dump_cause); + let mut result = Self { definitions, elements: elements.clone(), liveness_constraints: RegionValues::new( elements, num_region_variables, - TrackCauses(true), + TrackCauses(nll_dump_cause), ), inferred_values: None, constraints: Vec::new(), diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 79505405692..4aee48b979d 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -16,7 +16,7 @@ use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; /// 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>, mir: &mut Mir<'tcx>) { +pub fn renumber_mir<'tcx>(infcx: &InferCtxt<'_, '_, 'tcx>, mir: &mut Mir<'tcx>) { debug!("renumber_mir()"); debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count); @@ -24,26 +24,36 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut visitor.visit_mir(mir); } +/// Replaces all regions appearing in `value` with fresh inference +/// variables. +pub fn renumber_regions<'tcx, T>( + infcx: &InferCtxt<'_, '_, 'tcx>, + ty_context: TyContext, + value: &T, +) -> T +where + T: TypeFoldable<'tcx>, +{ + debug!("renumber_regions(value={:?})", value); + + infcx + .tcx + .fold_regions(value, &mut false, |_region, _depth| { + let origin = NLLRegionVariableOrigin::Inferred(ty_context); + infcx.next_nll_region_var(origin) + }) +} + struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, } impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { - /// Replaces all regions appearing in `value` with fresh inference - /// variables. This is what we do for almost the entire MIR, with - /// the exception of the declared types of our arguments. fn renumber_regions(&mut self, ty_context: TyContext, value: &T) -> T where T: TypeFoldable<'tcx>, { - debug!("renumber_regions(value={:?})", value); - - self.infcx - .tcx - .fold_regions(value, &mut false, |_region, _depth| { - let origin = NLLRegionVariableOrigin::Inferred(ty_context); - self.infcx.next_nll_region_var(origin) - }) + renumber_regions(self.infcx, ty_context, value) } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index b85573b9c07..4db8e3c793d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -17,9 +17,15 @@ //! `RETURN_PLACE` the MIR arguments) are always fully normalize (and //! contain revealed `impl Trait` values). +use borrow_check::nll::renumber; use borrow_check::nll::universal_regions::UniversalRegions; +use rustc::hir::def_id::DefId; +use rustc::infer::InferOk; use rustc::ty::Ty; +use rustc::ty::subst::Subst; use rustc::mir::*; +use rustc::mir::visit::TyContext; +use rustc::traits::PredicateObligations; use rustc_data_structures::indexed_vec::Idx; @@ -29,13 +35,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { pub(super) fn equate_inputs_and_outputs( &mut self, mir: &Mir<'tcx>, + mir_def_id: DefId, universal_regions: &UniversalRegions<'tcx>, ) { + let tcx = self.infcx.tcx; + let &UniversalRegions { unnormalized_output_ty, unnormalized_input_tys, .. } = universal_regions; + let infcx = self.infcx; let start_position = Location { block: START_BLOCK, @@ -52,10 +62,88 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // Return types are a bit more complex. They may contain existential `impl Trait` // types. - + debug!( + "equate_inputs_and_outputs: unnormalized_output_ty={:?}", + unnormalized_output_ty + ); let output_ty = self.normalize(&unnormalized_output_ty, start_position); + debug!( + "equate_inputs_and_outputs: normalized output_ty={:?}", + output_ty + ); let mir_output_ty = mir.local_decls[RETURN_PLACE].ty; - self.equate_normalized_input_or_output(start_position, output_ty, mir_output_ty); + let anon_type_map = self.fully_perform_op(start_position.at_self(), |cx| { + let mut obligations = ObligationAccumulator::default(); + + let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types( + mir_def_id, + cx.body_id, + cx.param_env, + &output_ty, + )); + debug!( + "equate_inputs_and_outputs: instantiated output_ty={:?}", + output_ty + ); + debug!( + "equate_inputs_and_outputs: anon_type_map={:#?}", + anon_type_map + ); + + debug!( + "equate_inputs_and_outputs: mir_output_ty={:?}", + mir_output_ty + ); + obligations.add(infcx + .at(&cx.misc(cx.last_span), cx.param_env) + .eq(output_ty, mir_output_ty)?); + + for (&anon_def_id, anon_decl) in &anon_type_map { + let anon_defn_ty = tcx.type_of(anon_def_id); + let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs); + let anon_defn_ty = renumber::renumber_regions( + cx.infcx, + TyContext::Location(start_position), + &anon_defn_ty, + ); + debug!( + "equate_inputs_and_outputs: concrete_ty={:?}", + anon_decl.concrete_ty + ); + debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty); + obligations.add(infcx + .at(&cx.misc(cx.last_span), cx.param_env) + .eq(anon_decl.concrete_ty, anon_defn_ty)?); + } + + debug!("equate_inputs_and_outputs: equated"); + + Ok(InferOk { + value: Some(anon_type_map), + obligations: obligations.into_vec(), + }) + }).unwrap_or_else(|terr| { + span_mirbug!( + self, + start_position, + "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`", + output_ty, + mir_output_ty, + terr + ); + None + }); + + // Finally + if let Some(anon_type_map) = anon_type_map { + self.fully_perform_op(start_position.at_self(), |_cx| { + infcx.constrain_anon_types(&anon_type_map, universal_regions); + Ok(InferOk { + value: (), + obligations: vec![], + }) + }).unwrap(); + } } fn equate_normalized_input_or_output(&mut self, location: Location, a: Ty<'tcx>, b: Ty<'tcx>) { @@ -73,3 +161,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } } + +#[derive(Debug, Default)] +struct ObligationAccumulator<'tcx> { + obligations: PredicateObligations<'tcx>, +} + +impl<'tcx> ObligationAccumulator<'tcx> { + fn add(&mut self, value: InferOk<'tcx, T>) -> T { + let InferOk { value, obligations } = value; + self.obligations.extend(obligations); + value + } + + fn into_vec(self) -> PredicateObligations<'tcx> { + self.obligations + } +} 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 48750b62611..4c8a171299f 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -17,6 +17,7 @@ use borrow_check::nll::universal_regions::UniversalRegions; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::MoveData; +use rustc::hir::def_id::DefId; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; use rustc::traits::{self, FulfillmentContext}; @@ -77,9 +78,9 @@ mod input_output; /// # 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 +/// - `mir_def_id` -- DefId from which the MIR is derived (must be local) /// - `region_bound_pairs` -- the implied outlives obligations between type parameters /// and lifetimes (e.g., `&'a T` implies `T: 'a`) /// - `implicit_region_bound` -- a region which all generic parameters are assumed @@ -94,14 +95,15 @@ mod input_output; /// - `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>, + mir_def_id: DefId, universal_regions: &UniversalRegions<'tcx>, liveness: &LivenessResults, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, ) -> MirTypeckRegionConstraints<'tcx> { + let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap(); let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); type_check_internal( infcx, @@ -113,7 +115,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( &mut |cx| { liveness::generate(cx, mir, liveness, flow_inits, move_data); - cx.equate_inputs_and_outputs(mir, universal_regions); + cx.equate_inputs_and_outputs(mir, mir_def_id, universal_regions); }, ) } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 070cea4bef2..6eca1b0a146 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -27,6 +27,7 @@ use rustc::hir::def_id::DefId; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; use rustc::infer::region_constraints::GenericKind; use rustc::infer::outlives::bounds::{self, OutlivesBound}; +use rustc::infer::outlives::free_region_map::FreeRegionRelations; use rustc::ty::{self, RegionVid, Ty, TyCtxt}; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; @@ -484,9 +485,6 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { let (unnormalized_output_ty, unnormalized_input_tys) = inputs_and_output.split_last().unwrap(); - // we should not have created any more variables - assert_eq!(self.infcx.num_region_vars(), num_universals); - debug!( "build: global regions = {}..{}", FIRST_GLOBAL_INDEX, @@ -793,3 +791,16 @@ impl<'tcx> UniversalRegionIndices<'tcx> { }) } } + +/// This trait is used by the `impl-trait` constraint code to abstract +/// over the `FreeRegionMap` from lexical regions and +/// `UniversalRegions` (from NLL)`. +impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegions<'tcx> { + fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool { + let shorter = shorter.to_region_vid(); + assert!(self.is_universal_region(shorter)); + let longer = longer.to_region_vid(); + assert!(self.is_universal_region(longer)); + self.outlives(longer, shorter) + } +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ef66920604b..14296e78ddd 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -985,7 +985,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, let ret_ty = fn_sig.output(); fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::SizedReturnType); - let ret_ty = fcx.instantiate_anon_types_from_return_value(&ret_ty); + let ret_ty = fcx.instantiate_anon_types_from_return_value(fn_id, &ret_ty); fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); fn_sig = fcx.tcx.mk_fn_sig( fn_sig.inputs().iter().cloned(), @@ -1880,11 +1880,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// function with type variables and records the `AnonTypeMap` for /// later use during writeback. See /// `InferCtxt::instantiate_anon_types` for more details. - fn instantiate_anon_types_from_return_value>(&self, value: &T) -> T { - debug!("instantiate_anon_types_from_return_value(value={:?})", value); + fn instantiate_anon_types_from_return_value>( + &self, + fn_id: ast::NodeId, + value: &T, + ) -> T { + let fn_def_id = self.tcx.hir.local_def_id(fn_id); + debug!( + "instantiate_anon_types_from_return_value(fn_def_id={:?}, value={:?})", + fn_def_id, + value + ); let (value, anon_type_map) = self.register_infer_ok_obligations( self.instantiate_anon_types( + fn_def_id, self.body_id, self.param_env, value, diff --git a/src/test/run-pass/impl-trait/example-calendar.rs b/src/test/run-pass/impl-trait/example-calendar.rs index 0b612c2d3ff..8d035bafab7 100644 --- a/src/test/run-pass/impl-trait/example-calendar.rs +++ b/src/test/run-pass/impl-trait/example-calendar.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: normal nll +//[nll] compile-flags: -Znll -Zborrowck=mir + #![feature(conservative_impl_trait, universal_impl_trait, fn_traits, diff --git a/src/test/ui/nll/ty-outlives/impl-trait-captures.rs b/src/test/ui/nll/ty-outlives/impl-trait-captures.rs new file mode 100644 index 00000000000..896b74b579b --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-captures.rs @@ -0,0 +1,27 @@ +// 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(conservative_impl_trait)] + +trait Foo<'a> { +} + +impl<'a, T> Foo<'a> for T { } + +fn foo<'a, T>(x: &T) -> impl Foo<'a> { + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR free region `'_#2r` does not outlive free region `ReEarlyBound(0, 'a)` +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr b/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr new file mode 100644 index 00000000000..7de994dae88 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -0,0 +1,14 @@ +warning: not reporting region error due to -Znll + --> $DIR/impl-trait-captures.rs:22:5 + | +22 | x + | ^ + +error: free region `'_#2r` does not outlive free region `ReEarlyBound(0, 'a)` + --> $DIR/impl-trait-captures.rs:22:5 + | +22 | x + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/impl-trait-outlives.rs b/src/test/ui/nll/ty-outlives/impl-trait-outlives.rs new file mode 100644 index 00000000000..c03ec839808 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-outlives.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 -Zverbose + +#![allow(warnings)] +#![feature(conservative_impl_trait)] + +use std::fmt::Debug; + +fn no_region<'a, T>(x: Box) -> impl Debug + 'a + //~^ WARNING not reporting region error due to -Znll +where + T: Debug, +{ + x + //~^ ERROR `T` does not outlive +} + +fn correct_region<'a, T>(x: Box) -> impl Debug + 'a +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box) -> impl Debug + 'a + //~^ WARNING not reporting region error due to -Znll +where + T: 'b + Debug, +{ + x + //~^ ERROR `T` does not outlive +} + +fn outlives_region<'a, 'b, T>(x: Box) -> impl Debug + 'a +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr b/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr new file mode 100644 index 00000000000..4ebd2c7fc43 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr @@ -0,0 +1,26 @@ +warning: not reporting region error due to -Znll + --> $DIR/impl-trait-outlives.rs:18:35 + | +18 | fn no_region<'a, T>(x: Box) -> impl Debug + 'a + | ^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/impl-trait-outlives.rs:34:42 + | +34 | fn wrong_region<'a, 'b, T>(x: Box) -> impl Debug + 'a + | ^^^^^^^^^^^^^^^ + +error: `T` does not outlive `'_#1r` + --> $DIR/impl-trait-outlives.rs:23:5 + | +23 | x + | ^ + +error: `T` does not outlive `'_#1r` + --> $DIR/impl-trait-outlives.rs:39:5 + | +39 | x + | ^ + +error: aborting due to 2 previous errors +