From a9cbfaa29687395452208a823502cc906a493ae2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 14 Mar 2018 17:29:09 -0400 Subject: [PATCH 1/8] rewrite to use a custom folder --- src/librustc/infer/anon_types/mod.rs | 92 +++++++++++++++------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/src/librustc/infer/anon_types/mod.rs b/src/librustc/infer/anon_types/mod.rs index eb26f0c1188bf..6a4f4072f53dc 100644 --- a/src/librustc/infer/anon_types/mod.rs +++ b/src/librustc/infer/anon_types/mod.rs @@ -14,8 +14,8 @@ use infer::outlives::free_region_map::FreeRegionRelations; use rustc_data_structures::fx::FxHashMap; use syntax::ast; use traits::{self, PredicateObligation}; -use ty::{self, Ty}; -use ty::fold::{BottomUpFolder, TypeFoldable}; +use ty::{self, Ty, TyCtxt}; +use ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder}; use ty::outlives::Component; use ty::subst::{Kind, UnpackedKind, Substs}; use util::nodemap::DefIdMap; @@ -458,55 +458,63 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, // after producing an error for each of them. - let definition_ty = gcx.fold_regions(&instantiated_ty, &mut false, |r, _| { - match *r { - // 'static and early-bound regions are valid. - ty::ReStatic | ty::ReEmpty => r, - - // All other regions, we map them appropriately to their adjusted - // indices, erroring if we find any lifetimes that were not mapped - // into the new set. - _ => if let Some(UnpackedKind::Lifetime(r1)) = map.get(&r.into()) - .map(|k| k.unpack()) { - r1 - } else { - // No mapping was found. This means that - // it is either a disallowed lifetime, - // which will be caught by regionck, or it - // is a region in a non-upvar closure - // generic, which is explicitly - // allowed. If that surprises you, read - // on. - // - // The case of closure is a somewhat - // subtle (read: hacky) consideration. The - // problem is that our closure types - // currently include all the lifetime - // parameters declared on the enclosing - // function, even if they are unused by - // the closure itself. We can't readily - // filter them out, so here we replace - // those values with `'empty`. This can't - // really make a difference to the rest of - // the compiler; those regions are ignored - // for the outlives relation, and hence - // don't affect trait selection or auto - // traits, and they are erased during - // trans. - gcx.types.re_empty - }, - } - }); - + let definition_ty = instantiated_ty.fold_with(&mut ReverseMapper { tcx: self.tcx, map }); debug!( "infer_anon_definition_from_instantiation: definition_ty={:?}", definition_ty ); + // We can unwrap here because our reverse mapper always + // produces things with 'gcx lifetime, though the type folder + // obscures that. + let definition_ty = gcx.lift(&definition_ty).unwrap(); + definition_ty } } +struct ReverseMapper<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + map: FxHashMap, Kind<'gcx>> +} + +impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx> { + fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> { + self.tcx + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + // ignore bound regions that appear in the type (e.g., this + // would ignore `'r` in a type like `for<'r> fn(&'r u32)`. + if let ty::ReLateBound(..) = *r { + return r; + } + + match self.map.get(&r.into()).map(|k| k.unpack()) { + Some(UnpackedKind::Lifetime(r1)) => r1, + Some(u) => panic!("region mapped to unexpected kind: {:?}", u), + None => { + // No mapping was found. This means that it is either a + // disallowed lifetime, which will be caught by regionck, + // or it is a region in a non-upvar closure generic, which + // is explicitly allowed. If that surprises you, read on. + // + // The case of closure is a somewhat subtle (read: hacky) + // consideration. The problem is that our closure types + // currently include all the lifetime parameters declared + // on the enclosing function, even if they are unused by + // the closure itself. We can't readily filter them out, + // so here we replace those values with `'empty`. This + // can't really make a difference to the rest of the + // compiler; those regions are ignored for the outlives + // relation, and hence don't affect trait selection or + // auto traits, and they are erased during trans. + self.tcx.types.re_empty + } + } + } +} + struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, parent_def_id: DefId, From fc3c90cf8af1a2c0e9e6167daed956905b5951a2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Mar 2018 05:10:42 -0400 Subject: [PATCH 2/8] report an error if we see an unexpected lifetime in impl Trait But leave closure substs alone. --- src/librustc/diagnostics.rs | 48 +++++ src/librustc/infer/anon_types/mod.rs | 188 ++++++++++++++---- .../region-escape-via-bound-contravariant.rs | 35 ++++ .../ui/impl-trait/region-escape-via-bound.rs | 34 ++++ .../impl-trait/region-escape-via-bound.stderr | 20 ++ 5 files changed, 291 insertions(+), 34 deletions(-) create mode 100644 src/test/ui/impl-trait/region-escape-via-bound-contravariant.rs create mode 100644 src/test/ui/impl-trait/region-escape-via-bound.rs create mode 100644 src/test/ui/impl-trait/region-escape-via-bound.stderr diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index b3a904f2f5fec..fad1803259286 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -2074,6 +2074,54 @@ a (non-transparent) struct containing a single float, while `Grams` is a transparent wrapper around a float. This can make a difference for the ABI. "##, +E0909: r##" +The `impl Trait` return type captures lifetime parameters that do not +appear within the `impl Trait` itself. + +Erroneous code example: + +```compile-fail,E0909 +use std::cell::Cell; + +trait Trait<'a> { } + +impl Trait<'b> for Cell<&'a u32> { } + +fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y> +where 'x: 'y +{ + x +} +``` + +Here, the function `foo` returns a value of type `Cell<&'x u32>`, +which references the lifetime `'x`. However, the return type is +declared as `impl Trait<'y>` -- this indicates that `foo` returns +"some type that implements `Trait<'y>`", but it also indicates that +the return type **only captures data referencing the lifetime `'y`**. +In this case, though, we are referencing data with lifetime `'x`, so +this function is in error. + +To fix this, you must reference the lifetime `'x` from the return +type. For example, changing the return type to `impl Trait<'y> + 'x` +would work: + +``` +use std::cell::Cell; + +trait Trait<'a> { } + +impl Trait<'b> for Cell<&'a u32> { } + +fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y> + 'x +where 'x: 'y +{ + x +} +``` +"##, + + } diff --git a/src/librustc/infer/anon_types/mod.rs b/src/librustc/infer/anon_types/mod.rs index 6a4f4072f53dc..93e8745af1b57 100644 --- a/src/librustc/infer/anon_types/mod.rs +++ b/src/librustc/infer/anon_types/mod.rs @@ -17,7 +17,7 @@ use traits::{self, PredicateObligation}; use ty::{self, Ty, TyCtxt}; use ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder}; use ty::outlives::Component; -use ty::subst::{Kind, UnpackedKind, Substs}; +use ty::subst::{Kind, Substs, UnpackedKind}; use util::nodemap::DefIdMap; pub type AnonTypeMap<'tcx> = DefIdMap>; @@ -113,10 +113,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ) -> InferOk<'tcx, (T, AnonTypeMap<'tcx>)> { debug!( "instantiate_anon_types(value={:?}, parent_def_id={:?}, body_id={:?}, param_env={:?})", - value, - parent_def_id, - body_id, - param_env, + value, parent_def_id, body_id, param_env, ); let mut instantiator = Instantiator { infcx: self, @@ -458,7 +455,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, // after producing an error for each of them. - let definition_ty = instantiated_ty.fold_with(&mut ReverseMapper { tcx: self.tcx, map }); + let definition_ty = + instantiated_ty.fold_with(&mut ReverseMapper::new( + self.tcx, + self.is_tainted_by_errors(), + def_id, + map, + instantiated_ty, + )); debug!( "infer_anon_definition_from_instantiation: definition_ty={:?}", definition_ty @@ -475,7 +479,49 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { struct ReverseMapper<'cx, 'gcx: 'tcx, 'tcx: 'cx> { tcx: TyCtxt<'cx, 'gcx, 'tcx>, - map: FxHashMap, Kind<'gcx>> + + /// If errors have already been reported in this fn, we suppress + /// our own errors because they are sometimes derivative. + tainted_by_errors: bool, + + anon_type_def_id: DefId, + map: FxHashMap, Kind<'gcx>>, + map_missing_regions_to_empty: bool, + + /// initially `Some`, set to `None` once error has been reported + hidden_ty: Option>, +} + +impl<'cx, 'gcx, 'tcx> ReverseMapper<'cx, 'gcx, 'tcx> { + fn new( + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + tainted_by_errors: bool, + anon_type_def_id: DefId, + map: FxHashMap, Kind<'gcx>>, + hidden_ty: Ty<'tcx>, + ) -> Self { + Self { + tcx, + tainted_by_errors, + anon_type_def_id, + map, + map_missing_regions_to_empty: false, + hidden_ty: Some(hidden_ty), + } + } + + fn fold_kind_mapping_missing_regions_to_empty(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> { + assert!(!self.map_missing_regions_to_empty); + self.map_missing_regions_to_empty = true; + let kind = kind.fold_with(self); + self.map_missing_regions_to_empty = false; + kind + } + + fn fold_kind_normally(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> { + assert!(!self.map_missing_regions_to_empty); + kind.fold_with(self) + } } impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx> { @@ -484,33 +530,105 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx> } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - // ignore bound regions that appear in the type (e.g., this - // would ignore `'r` in a type like `for<'r> fn(&'r u32)`. - if let ty::ReLateBound(..) = *r { - return r; + match r { + // ignore bound regions that appear in the type (e.g., this + // would ignore `'r` in a type like `for<'r> fn(&'r u32)`. + ty::ReLateBound(..) => return r, + + // ignore `'static`, as that can appear anywhere + ty::ReStatic => return r, + + _ => { } } match self.map.get(&r.into()).map(|k| k.unpack()) { Some(UnpackedKind::Lifetime(r1)) => r1, Some(u) => panic!("region mapped to unexpected kind: {:?}", u), None => { - // No mapping was found. This means that it is either a - // disallowed lifetime, which will be caught by regionck, - // or it is a region in a non-upvar closure generic, which - // is explicitly allowed. If that surprises you, read on. + if !self.map_missing_regions_to_empty && !self.tainted_by_errors { + if let Some(hidden_ty) = self.hidden_ty.take() { + let span = self.tcx.def_span(self.anon_type_def_id); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0909, + "hidden type for `impl Trait` captures lifetime that \ + does not appear in bounds", + ); + + // Assuming regionck succeeded, then we must + // be capturing *some* region from the fn + // header, and hence it must be free, so it's + // ok to invoke this fn (which doesn't accept + // all regions, and would ICE if an + // inappropriate region is given). We check + // `is_tainted_by_errors` by errors above, so + // we don't get in here unless regionck + // succeeded. (Note also that if regionck + // failed, then the regions we are attempting + // to map here may well be giving errors + // *because* the constraints were not + // satisfiable.) + self.tcx.note_and_explain_free_region( + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + r, + "" + ); + + err.emit(); + } + } + self.tcx.types.re_empty + }, + } + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.sty { + ty::TyClosure(def_id, substs) => { + // I am a horrible monster and I pray for death. When + // we encounter a closure here, it is always a closure + // from within the function that we are currently + // type-checking -- one that is now being encapsulated + // in an existential abstract type. Ideally, we would + // go through the types/lifetimes that it references + // and treat them just like we would any other type, + // which means we would error out if we find any + // reference to a type/region that is not in the + // "reverse map". // - // The case of closure is a somewhat subtle (read: hacky) - // consideration. The problem is that our closure types - // currently include all the lifetime parameters declared - // on the enclosing function, even if they are unused by - // the closure itself. We can't readily filter them out, + // **However,** in the case of closures, there is a + // somewhat subtle (read: hacky) consideration. The + // problem is that our closure types currently include + // all the lifetime parameters declared on the + // enclosing function, even if they are unused by the + // closure itself. We can't readily filter them out, // so here we replace those values with `'empty`. This // can't really make a difference to the rest of the - // compiler; those regions are ignored for the outlives - // relation, and hence don't affect trait selection or - // auto traits, and they are erased during trans. - self.tcx.types.re_empty + // compiler; those regions are ignored for the + // outlives relation, and hence don't affect trait + // selection or auto traits, and they are erased + // during trans. + + let generics = self.tcx.generics_of(def_id); + let parent_len = generics.parent_count(); + let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map( + |(index, &kind)| { + if index < parent_len { + // Accommodate missing regions in the parent kinds... + self.fold_kind_mapping_missing_regions_to_empty(kind) + } else { + // ...but not elsewhere. + self.fold_kind_normally(kind) + } + }, + )); + + self.tcx.mk_closure(def_id, ty::ClosureSubsts { substs }) } + + _ => ty.super_fold_with(self), } } } @@ -573,12 +691,13 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { 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); + debug!( + "instantiate_anon_types_in_map: \ + encountered anon with wrong parent \ + def_id={:?} \ + anon_parent_def_id={:?}", + def_id, anon_parent_def_id + ); } } @@ -598,8 +717,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { debug!( "instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})", - def_id, - substs + def_id, substs ); // Use the same type variable if the exact same TyAnon appears more @@ -608,8 +726,10 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { return anon_defn.concrete_ty; } let span = tcx.def_span(def_id); - let ty_var = infcx.next_ty_var(ty::UniverseIndex::ROOT, - TypeVariableOrigin::TypeInference(span)); + let ty_var = infcx.next_ty_var( + ty::UniverseIndex::ROOT, + TypeVariableOrigin::TypeInference(span), + ); let predicates_of = tcx.predicates_of(def_id); let bounds = predicates_of.instantiate(tcx, substs); diff --git a/src/test/ui/impl-trait/region-escape-via-bound-contravariant.rs b/src/test/ui/impl-trait/region-escape-via-bound-contravariant.rs new file mode 100644 index 0000000000000..416bdae517845 --- /dev/null +++ b/src/test/ui/impl-trait/region-escape-via-bound-contravariant.rs @@ -0,0 +1,35 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// In contrast to `region-escape-via-bound-invariant`, in this case we +// *can* return a value of type `&'x u32`, even though `'x` does not +// appear in the bounds. This is because `&` is contravariant, and so +// we are *actually* returning a `&'y u32`. +// +// See https://github.com/rust-lang/rust/issues/46541 for more details. + +// run-pass + +#![allow(dead_code)] +#![feature(conservative_impl_trait)] +#![feature(in_band_lifetimes)] +#![feature(nll)] + +trait Trait<'a> { } + +impl Trait<'b> for &'a u32 { } + +fn foo(x: &'x u32) -> impl Trait<'y> +where 'x: 'y +{ + x +} + +fn main() { } diff --git a/src/test/ui/impl-trait/region-escape-via-bound.rs b/src/test/ui/impl-trait/region-escape-via-bound.rs new file mode 100644 index 0000000000000..38c18ce61044e --- /dev/null +++ b/src/test/ui/impl-trait/region-escape-via-bound.rs @@ -0,0 +1,34 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we do not allow the region `'x` to escape in the impl +// trait **even though** `'y` escapes, which outlives `'x`. +// +// See https://github.com/rust-lang/rust/issues/46541 for more details. + +#![allow(dead_code)] +#![feature(conservative_impl_trait)] +#![feature(in_band_lifetimes)] +#![feature(nll)] + +use std::cell::Cell; + +trait Trait<'a> { } + +impl Trait<'b> for Cell<&'a u32> { } + +fn foo(x: Cell<&'x u32>) -> impl Trait<'y> + //~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds [E0909] +where 'x: 'y +{ + x +} + +fn main() { } diff --git a/src/test/ui/impl-trait/region-escape-via-bound.stderr b/src/test/ui/impl-trait/region-escape-via-bound.stderr new file mode 100644 index 0000000000000..5659fee9bedc6 --- /dev/null +++ b/src/test/ui/impl-trait/region-escape-via-bound.stderr @@ -0,0 +1,20 @@ +error[E0909]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/region-escape-via-bound.rs:27:29 + | +LL | fn foo(x: Cell<&'x u32>) -> impl Trait<'y> + | ^^^^^^^^^^^^^^ + | +note: hidden type `std::cell::Cell<&'x u32>` captures the lifetime 'x as defined on the function body at 27:1 + --> $DIR/region-escape-via-bound.rs:27:1 + | +LL | / fn foo(x: Cell<&'x u32>) -> impl Trait<'y> +LL | | //~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds [E0909] +LL | | where 'x: 'y +LL | | { +LL | | x +LL | | } + | |_^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0909`. From f71de45b230954c25f2337272852be1146d26136 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Mar 2018 10:49:05 -0400 Subject: [PATCH 3/8] use subtyping when we create a closure instead of for upvar types We used to make the upvar types in the closure `==` but that was stronger than we needed. Subtyping suffices, since we are copying the upvar value into the closure field. This in turn allows us to infer smaller lifetimes in captured values in some cases (like the example here), avoiding errors. --- src/librustc_typeck/check/upvar.rs | 2 +- .../ui/generator/auto-trait-regions.stderr | 6 ++-- ...-escape-via-bound-contravariant-closure.rs | 31 +++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/impl-trait/region-escape-via-bound-contravariant-closure.rs diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index ab148afafbe09..4fc3344dab2a9 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -217,7 +217,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .upvar_tys(closure_def_id, self.tcx) .zip(final_upvar_tys) { - self.demand_eqtype(span, final_upvar_ty, upvar_ty); + self.demand_suptype(span, upvar_ty, final_upvar_ty); } // If we are also inferred the closure kind here, diff --git a/src/test/ui/generator/auto-trait-regions.stderr b/src/test/ui/generator/auto-trait-regions.stderr index cd83915fe82d3..dd78baf927509 100644 --- a/src/test/ui/generator/auto-trait-regions.stderr +++ b/src/test/ui/generator/auto-trait-regions.stderr @@ -1,15 +1,15 @@ -error[E0277]: the trait bound `No: Foo` is not satisfied in `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&'static OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]` +error[E0277]: the trait bound `No: Foo` is not satisfied in `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]` --> $DIR/auto-trait-regions.rs:40:5 | LL | assert_foo(gen); //~ ERROR the trait bound `No: Foo` is not satisfied - | ^^^^^^^^^^ within `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&'static OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]`, the trait `Foo` is not implemented for `No` + | ^^^^^^^^^^ within `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]`, the trait `Foo` is not implemented for `No` | = help: the following implementations were found: = note: required because it appears within the type `OnlyFooIfStaticRef` = note: required because it appears within the type `&OnlyFooIfStaticRef` = note: required because it appears within the type `for<'r> {&'r OnlyFooIfStaticRef, ()}` - = note: required because it appears within the type `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&'static OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]` + = note: required because it appears within the type `[generator@$DIR/auto-trait-regions.rs:35:15: 39:6 x:&&OnlyFooIfStaticRef for<'r> {&'r OnlyFooIfStaticRef, ()}]` note: required by `assert_foo` --> $DIR/auto-trait-regions.rs:30:1 | diff --git a/src/test/ui/impl-trait/region-escape-via-bound-contravariant-closure.rs b/src/test/ui/impl-trait/region-escape-via-bound-contravariant-closure.rs new file mode 100644 index 0000000000000..f554efe903613 --- /dev/null +++ b/src/test/ui/impl-trait/region-escape-via-bound-contravariant-closure.rs @@ -0,0 +1,31 @@ +// 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. + +// In contrast to `region-escape-via-bound-invariant`, in this case we +// *can* return a value of type `&'x u32`, even though `'x` does not +// appear in the bounds. This is because `&` is contravariant, and so +// we are *actually* returning a `&'y u32`. +// +// See https://github.com/rust-lang/rust/issues/46541 for more details. + +// run-pass + +#![allow(dead_code)] +#![feature(conservative_impl_trait)] +#![feature(in_band_lifetimes)] +#![feature(nll)] + +fn foo(x: &'x u32) -> impl Fn() -> &'y u32 +where 'x: 'y +{ + move || x +} + +fn main() { } From 9d5ec9ef1a03d343d01bde68ed6e6affc32ae492 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Mar 2018 16:17:27 -0400 Subject: [PATCH 4/8] work around fallout from these changes in rustc --- src/librustc/infer/canonical.rs | 3 ++- src/librustc/lib.rs | 1 + .../traits/specialize/specialization_graph.rs | 18 ++++++++++------ src/librustc/ty/mod.rs | 21 ++++++++++++------- src/librustc/ty/sty.rs | 11 ++++++---- src/librustc/util/captures.rs | 18 ++++++++++++++++ src/librustc_metadata/decoder.rs | 6 +++++- src/librustc_typeck/collect.rs | 5 +++-- 8 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 src/librustc/util/captures.rs diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs index 4e0cf59e8a7fd..22526c7751d50 100644 --- a/src/librustc/infer/canonical.rs +++ b/src/librustc/infer/canonical.rs @@ -40,6 +40,7 @@ use traits::{Obligation, ObligationCause, PredicateObligation}; use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags}; use ty::subst::{Kind, UnpackedKind}; use ty::fold::{TypeFoldable, TypeFolder}; +use util::captures::Captures; use util::common::CellUsizeExt; use rustc_data_structures::indexed_vec::IndexVec; @@ -382,7 +383,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { param_env: ty::ParamEnv<'tcx>, unsubstituted_region_constraints: &'a QueryRegionConstraints<'tcx>, result_subst: &'a CanonicalVarValues<'tcx>, - ) -> impl Iterator> + 'a { + ) -> impl Iterator> + Captures<'gcx> + 'a { let QueryRegionConstraints { region_outlives, ty_outlives, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 77259f156e5e2..22b07c8cc044f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -157,6 +157,7 @@ pub mod traits; pub mod ty; pub mod util { + pub mod captures; pub mod common; pub mod ppaux; pub mod nodemap; diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs index e0d662657b7de..e56a8662f3eb4 100644 --- a/src/librustc/traits/specialize/specialization_graph.rs +++ b/src/librustc/traits/specialize/specialization_graph.rs @@ -19,6 +19,7 @@ use ty::{self, TyCtxt, TypeFoldable}; use ty::fast_reject::{self, SimplifiedType}; use rustc_data_structures::sync::Lrc; use syntax::ast::Name; +use util::captures::Captures; use util::nodemap::{DefIdMap, FxHashMap}; /// A per-trait graph of impls in specialization order. At the moment, this @@ -313,9 +314,10 @@ impl<'a, 'gcx, 'tcx> Node { } /// Iterate over the items defined directly by the given (impl or trait) node. - #[inline] // FIXME(#35870) Avoid closures being unexported due to impl Trait. - pub fn items(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) - -> impl Iterator + 'a { + pub fn items( + &self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + ) -> impl Iterator + 'a { tcx.associated_items(self.def_id()) } @@ -367,9 +369,13 @@ impl<'a, 'gcx, 'tcx> Ancestors { /// Search the items from the given ancestors, returning each definition /// with the given name and the given kind. #[inline] // FIXME(#35870) Avoid closures being unexported due to impl Trait. - pub fn defs(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, trait_item_name: Name, - trait_item_kind: ty::AssociatedKind, trait_def_id: DefId) - -> impl Iterator> + 'a { + pub fn defs( + self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + trait_item_name: Name, + trait_item_kind: ty::AssociatedKind, + trait_def_id: DefId, + ) -> impl Iterator> + Captures<'gcx> + Captures<'tcx> + 'a { self.flat_map(move |node| { node.items(tcx).filter(move |impl_item| { impl_item.kind == trait_item_kind && diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index c915022351925..95c5cd377d71f 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -34,6 +34,7 @@ use ty; use ty::subst::{Subst, Substs}; use ty::util::{IntTypeExt, Discr}; use ty::walk::TypeWalker; +use util::captures::Captures; use util::nodemap::{NodeSet, DefIdMap, FxHashMap}; use serialize::{self, Encodable, Encoder}; @@ -1942,8 +1943,10 @@ impl<'a, 'gcx, 'tcx> AdtDef { } #[inline] - pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>) - -> impl Iterator> + 'a { + pub fn discriminants( + &'a self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + ) -> impl Iterator> + Captures<'gcx> + 'a { let repr_type = self.repr.discr_type(); let initial = repr_type.initial_discriminant(tcx.global_tcx()); let mut prev_discr = None::>; @@ -2290,7 +2293,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Returns an iterator of the def-ids for all body-owners in this /// crate. If you would prefer to iterate over the bodies /// themselves, you can do `self.hir.krate().body_ids.iter()`. - pub fn body_owners(self) -> impl Iterator + 'a { + pub fn body_owners( + self, + ) -> impl Iterator + Captures<'tcx> + Captures<'gcx> + 'a { self.hir.krate() .body_ids .iter() @@ -2394,11 +2399,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - #[inline] // FIXME(#35870) Avoid closures being unexported due to impl Trait. - pub fn associated_items(self, def_id: DefId) - -> impl Iterator + 'a { + pub fn associated_items( + self, + def_id: DefId, + ) -> impl Iterator + 'a { let def_ids = self.associated_item_def_ids(def_id); - (0..def_ids.len()).map(move |i| self.associated_item(def_ids[i])) + Box::new((0..def_ids.len()).map(move |i| self.associated_item(def_ids[i]))) + as Box + 'a> } /// Returns true if the impls are the same polarity and are implementing diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index bb5c7b5fd2a5e..d7ab6e39ac5f0 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -18,6 +18,7 @@ use rustc_data_structures::indexed_vec::Idx; use ty::subst::{Substs, Subst, Kind, UnpackedKind}; use ty::{self, AdtDef, TypeFlags, Ty, TyCtxt, TypeFoldable}; use ty::{Slice, TyS}; +use util::captures::Captures; use std::iter; use std::cmp::Ordering; @@ -384,9 +385,11 @@ impl<'a, 'gcx, 'tcx> ClosureSubsts<'tcx> { /// This returns the types of the MIR locals which had to be stored across suspension points. /// It is calculated in rustc_mir::transform::generator::StateTransform. /// All the types here must be in the tuple in GeneratorInterior. - pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> - impl Iterator> + 'a - { + pub fn state_tys( + self, + def_id: DefId, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + ) -> impl Iterator> + Captures<'gcx> + 'a { let state = tcx.generator_layout(def_id).fields.iter(); state.map(move |d| d.ty.subst(tcx, self.substs)) } @@ -403,7 +406,7 @@ impl<'a, 'gcx, 'tcx> ClosureSubsts<'tcx> { /// This is the types of all the fields stored in a generator. /// It includes the upvars, state types and the state discriminant which is u32. pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> - impl Iterator> + 'a + impl Iterator> + Captures<'gcx> + 'a { self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx)) } diff --git a/src/librustc/util/captures.rs b/src/librustc/util/captures.rs new file mode 100644 index 0000000000000..b68cfd278fa9e --- /dev/null +++ b/src/librustc/util/captures.rs @@ -0,0 +1,18 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// "Signaling" trait used in impl trait to tag lifetimes that you may +/// need to capture but don't really need for other reasons. +/// Basically a workaround; see [this comment] for details. +/// +/// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999 +pub trait Captures<'a> { } + +impl<'a, T: ?Sized> Captures<'a> for T { } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index e1c17e8260a79..b0c945fbf2a05 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -29,6 +29,7 @@ use rustc::session::Session; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::codec::TyDecoder; use rustc::mir::Mir; +use rustc::util::captures::Captures; use rustc::util::nodemap::FxHashMap; use std::collections::BTreeMap; @@ -146,7 +147,10 @@ impl<'a, 'tcx: 'a, T: Decodable> Lazy { } impl<'a, 'tcx: 'a, T: Decodable> LazySeq { - pub fn decode>(self, meta: M) -> impl Iterator + 'a { + pub fn decode>( + self, + meta: M, + ) -> impl Iterator + Captures<'tcx> + 'a { let mut dcx = meta.decoder(self.position); dcx.lazy_state = LazyState::NodeStart(self.position); (0..self.len).map(move |_| T::decode(&mut dcx).unwrap()) diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index a17b35dec42d7..6f24d06844bb4 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -35,8 +35,9 @@ use rustc::ty::{ToPredicate, ReprOptions}; use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::util::IntTypeExt; -use rustc::util::nodemap::{FxHashSet, FxHashMap}; use rustc::ty::util::Discr; +use rustc::util::captures::Captures; +use rustc::util::nodemap::{FxHashSet, FxHashMap}; use syntax::{abi, ast}; use syntax::ast::MetaItemKind; @@ -1281,7 +1282,7 @@ fn is_unsized<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, fn early_bound_lifetimes_from_generics<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, ast_generics: &'a hir::Generics) - -> impl Iterator + -> impl Iterator + Captures<'tcx> { ast_generics .lifetimes() From 94eebaa32505919a17c0848ad0663d49bcddfa80 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Mar 2018 09:24:32 -0400 Subject: [PATCH 5/8] WIP fix mir-opt-end-region-8 --- src/test/mir-opt/end_region_8.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/mir-opt/end_region_8.rs b/src/test/mir-opt/end_region_8.rs index 405864aba9436..d621cdb4d58f8 100644 --- a/src/test/mir-opt/end_region_8.rs +++ b/src/test/mir-opt/end_region_8.rs @@ -36,7 +36,7 @@ fn foo(f: F) where F: FnOnce() -> i32 { // let _2: &'21_1rs D; // ... // let mut _3: (); -// let mut _4: [closure@NodeId(22) r:&'21_1rs D]; +// let mut _4: [closure@NodeId(22) r:&'19s D]; // let mut _5: &'21_1rs D; // bb0: { // StorageLive(_1); @@ -54,6 +54,7 @@ fn foo(f: F) where F: FnOnce() -> i32 { // resume; // } // bb2: { +// EndRegion('19s); // StorageDead(_4); // _0 = (); // EndRegion('21_1rs); @@ -61,6 +62,7 @@ fn foo(f: F) where F: FnOnce() -> i32 { // drop(_1) -> [return: bb4, unwind: bb1]; // } // bb3: { +// EndRegion('19s); // EndRegion('21_1rs); // drop(_1) -> bb1; // } @@ -72,7 +74,7 @@ fn foo(f: F) where F: FnOnce() -> i32 { // END rustc.main.SimplifyCfg-qualify-consts.after.mir // START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir -// fn main::{{closure}}(_1: [closure@NodeId(22) r:&'21_1rs D]) -> i32 { +// fn main::{{closure}}(_1: [closure@NodeId(22) r:&'19s D]) -> i32 { // let mut _0: i32; // let mut _2: i32; // From 395570857686648f76ba801d3cde24e4cb906934 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Mar 2018 16:31:20 -0400 Subject: [PATCH 6/8] WIP tweak example to include feature gate --- src/librustc/diagnostics.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index fad1803259286..4c2dd8c271862 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -2081,6 +2081,8 @@ appear within the `impl Trait` itself. Erroneous code example: ```compile-fail,E0909 +#![feature(conservative_impl_trait)] + use std::cell::Cell; trait Trait<'a> { } From 48c4e352d38479a5adcd664bef3a208640ba5cbc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Mar 2018 19:22:41 -0400 Subject: [PATCH 7/8] WIP do not use in-band lifetimes --- src/librustc/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 4c2dd8c271862..10156d22501e7 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -2087,7 +2087,7 @@ use std::cell::Cell; trait Trait<'a> { } -impl Trait<'b> for Cell<&'a u32> { } +impl<'a, 'b> Trait<'b> for Cell<&'a u32> { } fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y> where 'x: 'y From 2e8a1abc2df44d8e71e52bf92b658438707564ea Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Mar 2018 19:23:29 -0400 Subject: [PATCH 8/8] also fix the Fixed code --- src/librustc/diagnostics.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 10156d22501e7..2fd875c344767 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -2109,11 +2109,13 @@ type. For example, changing the return type to `impl Trait<'y> + 'x` would work: ``` +#![feature(conservative_impl_trait)] + use std::cell::Cell; trait Trait<'a> { } -impl Trait<'b> for Cell<&'a u32> { } +impl<'a,'b> Trait<'b> for Cell<&'a u32> { } fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y> + 'x where 'x: 'y