Auto merge of #49041 - nikomatsakis:issue-46541-impl-trait-hidden-lifetimes, r=cramertj
Detect illegal hidden lifetimes in `impl Trait` This branch fixes #46541 -- however, it presently doesn't build because it also *breaks* a number of existing usages of impl Trait. I'm opening it as a WIP for now, just because we want to move on impl Trait, but I'll try to fix the problem in a bit. ~~(The problem is due to the fact that we apparently infer stricter lifetimes in closures that we need to; for example, if you capture a variable of type `&'a &'b u32`, we will put *precisely* those lifetimes into the closure, even if the closure would be happy with `&'a &'a u32`. This causes the present chance to affect things that are not invariant.)~~ fixed r? @cramertj
This commit is contained in:
commit
e575773141
17 changed files with 427 additions and 84 deletions
|
@ -2074,6 +2074,58 @@ 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
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
trait Trait<'a> { }
|
||||
|
||||
impl<'a, 'b> 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:
|
||||
|
||||
```
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
trait Trait<'a> { }
|
||||
|
||||
impl<'a,'b> Trait<'b> for Cell<&'a u32> { }
|
||||
|
||||
fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y> + 'x
|
||||
where 'x: 'y
|
||||
{
|
||||
x
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ 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 ty::subst::{Kind, Substs, UnpackedKind};
|
||||
use util::nodemap::DefIdMap;
|
||||
|
||||
pub type AnonTypeMap<'tcx> = DefIdMap<AnonTypeDecl<'tcx>>;
|
||||
|
@ -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,55 +455,184 @@ 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::new(
|
||||
self.tcx,
|
||||
self.is_tainted_by_errors(),
|
||||
def_id,
|
||||
map,
|
||||
instantiated_ty,
|
||||
));
|
||||
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>,
|
||||
|
||||
/// 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<'tcx>, Kind<'gcx>>,
|
||||
map_missing_regions_to_empty: bool,
|
||||
|
||||
/// initially `Some`, set to `None` once error has been reported
|
||||
hidden_ty: Option<Ty<'tcx>>,
|
||||
}
|
||||
|
||||
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<'tcx>, 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> {
|
||||
fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
|
||||
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 => {
|
||||
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".
|
||||
//
|
||||
// **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.
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
parent_def_id: DefId,
|
||||
|
@ -565,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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -590,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
|
||||
|
@ -600,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);
|
||||
|
|
|
@ -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<Item = PredicateObligation<'tcx>> + 'a {
|
||||
) -> impl Iterator<Item = PredicateObligation<'tcx>> + Captures<'gcx> + 'a {
|
||||
let QueryRegionConstraints {
|
||||
region_outlives,
|
||||
ty_outlives,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Item = ty::AssociatedItem> + 'a {
|
||||
pub fn items(
|
||||
&self,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
) -> impl Iterator<Item = ty::AssociatedItem> + '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<Item = NodeItem<ty::AssociatedItem>> + '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<Item = NodeItem<ty::AssociatedItem>> + Captures<'gcx> + Captures<'tcx> + 'a {
|
||||
self.flat_map(move |node| {
|
||||
node.items(tcx).filter(move |impl_item| {
|
||||
impl_item.kind == trait_item_kind &&
|
||||
|
|
|
@ -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<Item=Discr<'tcx>> + 'a {
|
||||
pub fn discriminants(
|
||||
&'a self,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
) -> impl Iterator<Item=Discr<'tcx>> + Captures<'gcx> + 'a {
|
||||
let repr_type = self.repr.discr_type();
|
||||
let initial = repr_type.initial_discriminant(tcx.global_tcx());
|
||||
let mut prev_discr = None::<Discr<'tcx>>;
|
||||
|
@ -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<Item = DefId> + 'a {
|
||||
pub fn body_owners(
|
||||
self,
|
||||
) -> impl Iterator<Item = DefId> + 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<Item = ty::AssociatedItem> + 'a {
|
||||
pub fn associated_items(
|
||||
self,
|
||||
def_id: DefId,
|
||||
) -> impl Iterator<Item = ty::AssociatedItem> + '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<dyn Iterator<Item = ty::AssociatedItem> + 'a>
|
||||
}
|
||||
|
||||
/// Returns true if the impls are the same polarity and are implementing
|
||||
|
|
|
@ -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<Item=Ty<'tcx>> + 'a
|
||||
{
|
||||
pub fn state_tys(
|
||||
self,
|
||||
def_id: DefId,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
) -> impl Iterator<Item=Ty<'tcx>> + 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<Item=Ty<'tcx>> + 'a
|
||||
impl Iterator<Item=Ty<'tcx>> + Captures<'gcx> + 'a
|
||||
{
|
||||
self.pre_transforms_tys(def_id, tcx).chain(self.state_tys(def_id, tcx))
|
||||
}
|
||||
|
|
18
src/librustc/util/captures.rs
Normal file
18
src/librustc/util/captures.rs
Normal file
|
@ -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 <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.
|
||||
|
||||
/// "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 { }
|
|
@ -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<T> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx: 'a, T: Decodable> LazySeq<T> {
|
||||
pub fn decode<M: Metadata<'a, 'tcx>>(self, meta: M) -> impl Iterator<Item = T> + 'a {
|
||||
pub fn decode<M: Metadata<'a, 'tcx>>(
|
||||
self,
|
||||
meta: M,
|
||||
) -> impl Iterator<Item = T> + 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())
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<Item=&'a hir::LifetimeDef>
|
||||
-> impl Iterator<Item=&'a hir::LifetimeDef> + Captures<'tcx>
|
||||
{
|
||||
ast_generics
|
||||
.lifetimes()
|
||||
|
|
|
@ -36,7 +36,7 @@ fn foo<F>(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: F) where F: FnOnce() -> i32 {
|
|||
// resume;
|
||||
// }
|
||||
// bb2: {
|
||||
// EndRegion('19s);
|
||||
// StorageDead(_4);
|
||||
// _0 = ();
|
||||
// EndRegion('21_1rs);
|
||||
|
@ -61,6 +62,7 @@ fn foo<F>(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: 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;
|
||||
//
|
||||
|
|
|
@ -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:
|
||||
<No as Foo>
|
||||
= 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
|
||||
|
|
||||
|
|
|
@ -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 <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.
|
||||
|
||||
// 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() { }
|
|
@ -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 <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.
|
||||
|
||||
// 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() { }
|
34
src/test/ui/impl-trait/region-escape-via-bound.rs
Normal file
34
src/test/ui/impl-trait/region-escape-via-bound.rs
Normal file
|
@ -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 <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.
|
||||
|
||||
// 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() { }
|
20
src/test/ui/impl-trait/region-escape-via-bound.stderr
Normal file
20
src/test/ui/impl-trait/region-escape-via-bound.stderr
Normal file
|
@ -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`.
|
Loading…
Add table
Reference in a new issue