mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
Fix impl Trait Lifetime Handling
After this change, impl Trait existentials are desugared to a new `abstract type` definition paired with a set of lifetimes to apply. In-scope generics are included as parents of the `abstract type` generics. Parent regions are replaced with static, and parent regions referenced in the `impl Trait` type are duplicated at the end of the `abstract type`'s generics.
This commit is contained in:
parent
d0f8e2913a
commit
bc4810d907
@ -2019,4 +2019,5 @@ register_diagnostics! {
|
||||
E0628, // generators cannot have explicit arguments
|
||||
E0631, // type mismatch in closure arguments
|
||||
E0637, // "'_" is not a valid lifetime bound
|
||||
E0657, // `impl Trait` can only capture lifetimes bound at the fn level
|
||||
}
|
||||
|
@ -591,8 +591,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
|
||||
}
|
||||
visitor.visit_lifetime(lifetime);
|
||||
}
|
||||
TyImplTraitExistential(ref bounds) => {
|
||||
TyImplTraitExistential(ref existty, ref lifetimes) => {
|
||||
let ExistTy { ref generics, ref bounds } = *existty;
|
||||
walk_generics(visitor, generics);
|
||||
walk_list!(visitor, visit_ty_param_bound, bounds);
|
||||
walk_list!(visitor, visit_lifetime, lifetimes);
|
||||
}
|
||||
TyImplTraitUniversal(_, ref bounds) => {
|
||||
walk_list!(visitor, visit_ty_param_bound, bounds);
|
||||
|
@ -42,8 +42,9 @@
|
||||
|
||||
use dep_graph::DepGraph;
|
||||
use hir;
|
||||
use hir::map::{Definitions, DefKey};
|
||||
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX};
|
||||
use hir::HirVec;
|
||||
use hir::map::{Definitions, DefKey, DefPathData};
|
||||
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX, DefIndexAddressSpace};
|
||||
use hir::def::{Def, PathResolution};
|
||||
use lint::builtin::PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES;
|
||||
use middle::cstore::CrateStore;
|
||||
@ -52,7 +53,7 @@ use session::Session;
|
||||
use util::common::FN_OUTPUT_NAME;
|
||||
use util::nodemap::{DefIdMap, FxHashMap, NodeMap};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::fmt::Debug;
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
@ -777,7 +778,24 @@ impl<'a> LoweringContext<'a> {
|
||||
t.span, GateIssue::Language,
|
||||
"`impl Trait` in return position is experimental");
|
||||
}
|
||||
hir::TyImplTraitExistential(self.lower_bounds(bounds, itctx))
|
||||
let def_index = self.resolver.definitions().opt_def_index(t.id).unwrap();
|
||||
let hir_bounds = self.lower_bounds(bounds, itctx);
|
||||
let (lifetimes, lifetime_defs) =
|
||||
self.lifetimes_from_impl_trait_bounds(def_index, &hir_bounds);
|
||||
|
||||
hir::TyImplTraitExistential(hir::ExistTy {
|
||||
generics: hir::Generics {
|
||||
lifetimes: lifetime_defs,
|
||||
// Type parameters are taken from environment:
|
||||
ty_params: Vec::new().into(),
|
||||
where_clause: hir::WhereClause {
|
||||
id: self.next_id().node_id,
|
||||
predicates: Vec::new().into(),
|
||||
},
|
||||
span: t.span,
|
||||
},
|
||||
bounds: hir_bounds,
|
||||
}, lifetimes)
|
||||
},
|
||||
ImplTraitContext::Universal(def_id) => {
|
||||
let has_feature = self.sess.features.borrow().universal_impl_trait;
|
||||
@ -808,6 +826,111 @@ impl<'a> LoweringContext<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn lifetimes_from_impl_trait_bounds(
|
||||
&mut self,
|
||||
parent_index: DefIndex,
|
||||
bounds: &hir::TyParamBounds
|
||||
) -> (HirVec<hir::Lifetime>, HirVec<hir::LifetimeDef>) {
|
||||
|
||||
// This visitor walks over impl trait bounds and creates defs for all lifetimes which
|
||||
// appear in the bounds, excluding lifetimes that are created within the bounds.
|
||||
// e.g. 'a, 'b, but not 'c in `impl for<'c> SomeTrait<'a, 'b, 'c>`
|
||||
struct ImplTraitLifetimeCollector<'r, 'a: 'r> {
|
||||
context: &'r mut LoweringContext<'a>,
|
||||
parent: DefIndex,
|
||||
currently_bound_lifetimes: Vec<Name>,
|
||||
already_defined_lifetimes: HashSet<Name>,
|
||||
output_lifetimes: Vec<hir::Lifetime>,
|
||||
output_lifetime_defs: Vec<hir::LifetimeDef>,
|
||||
}
|
||||
|
||||
impl<'r, 'a: 'r, 'v> hir::intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r, 'a> {
|
||||
fn nested_visit_map<'this>(&'this mut self)
|
||||
-> hir::intravisit::NestedVisitorMap<'this, 'v> {
|
||||
hir::intravisit::NestedVisitorMap::None
|
||||
}
|
||||
|
||||
fn visit_poly_trait_ref(&mut self,
|
||||
polytr: &'v hir::PolyTraitRef,
|
||||
_: hir::TraitBoundModifier) {
|
||||
let old_len = self.currently_bound_lifetimes.len();
|
||||
|
||||
// Record the introduction of 'a in `for<'a> ...`
|
||||
for lt_def in &polytr.bound_lifetimes {
|
||||
// Introduce lifetimes one at a time so that we can handle
|
||||
// cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd> ...`
|
||||
if let hir::LifetimeName::Name(name) = lt_def.lifetime.name {
|
||||
self.currently_bound_lifetimes.push(name);
|
||||
}
|
||||
|
||||
// Visit the lifetime bounds
|
||||
for lt_bound in <_def.bounds {
|
||||
self.visit_lifetime(<_bound);
|
||||
}
|
||||
}
|
||||
|
||||
hir::intravisit::walk_trait_ref(self, &polytr.trait_ref);
|
||||
|
||||
self.currently_bound_lifetimes.truncate(old_len);
|
||||
}
|
||||
|
||||
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
|
||||
// Exclude '_, 'static, and elided lifetimes (there should be no elided lifetimes)
|
||||
if let hir::LifetimeName::Name(lifetime_name) = lifetime.name {
|
||||
if !self.currently_bound_lifetimes.contains(&lifetime_name) &&
|
||||
!self.already_defined_lifetimes.contains(&lifetime_name)
|
||||
{
|
||||
self.already_defined_lifetimes.insert(lifetime_name);
|
||||
let name = hir::LifetimeName::Name(lifetime_name);
|
||||
|
||||
self.output_lifetimes.push(hir::Lifetime {
|
||||
id: self.context.next_id().node_id,
|
||||
span: lifetime.span,
|
||||
name,
|
||||
});
|
||||
|
||||
let def_node_id = self.context.next_id().node_id;
|
||||
self.context.resolver.definitions().create_def_with_parent(
|
||||
self.parent,
|
||||
def_node_id,
|
||||
DefPathData::LifetimeDef(lifetime_name.as_str()),
|
||||
DefIndexAddressSpace::High,
|
||||
Mark::root()
|
||||
);
|
||||
let def_lifetime = hir::Lifetime {
|
||||
id: def_node_id,
|
||||
span: lifetime.span,
|
||||
name,
|
||||
};
|
||||
self.output_lifetime_defs.push(hir::LifetimeDef {
|
||||
lifetime: def_lifetime,
|
||||
bounds: Vec::new().into(),
|
||||
pure_wrt_drop: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut lifetime_collector = ImplTraitLifetimeCollector {
|
||||
context: self,
|
||||
parent: parent_index,
|
||||
currently_bound_lifetimes: Vec::new(),
|
||||
already_defined_lifetimes: HashSet::new(),
|
||||
output_lifetimes: Vec::new(),
|
||||
output_lifetime_defs: Vec::new(),
|
||||
};
|
||||
|
||||
for bound in bounds {
|
||||
hir::intravisit::walk_ty_param_bound(&mut lifetime_collector, &bound);
|
||||
}
|
||||
|
||||
(
|
||||
lifetime_collector.output_lifetimes.into(),
|
||||
lifetime_collector.output_lifetime_defs.into()
|
||||
)
|
||||
}
|
||||
|
||||
fn lower_foreign_mod(&mut self, fm: &ForeignMod) -> hir::ForeignMod {
|
||||
hir::ForeignMod {
|
||||
abi: fm.abi,
|
||||
|
@ -1436,6 +1436,12 @@ pub struct BareFnTy {
|
||||
pub arg_names: HirVec<Spanned<Name>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||
pub struct ExistTy {
|
||||
pub generics: Generics,
|
||||
pub bounds: TyParamBounds,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||
/// The different kinds of types recognized by the compiler
|
||||
pub enum Ty_ {
|
||||
@ -1463,7 +1469,16 @@ pub enum Ty_ {
|
||||
TyTraitObject(HirVec<PolyTraitRef>, Lifetime),
|
||||
/// An exsitentially quantified (there exists a type satisfying) `impl
|
||||
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
|
||||
TyImplTraitExistential(TyParamBounds),
|
||||
///
|
||||
/// The `ExistTy` structure emulates an
|
||||
/// `abstract type Foo<'a, 'b>: MyTrait<'a, 'b>;`.
|
||||
///
|
||||
/// The `HirVec<Lifetime>` is the list of lifetimes applied as parameters
|
||||
/// to the `abstract type`, e.g. the `'c` and `'d` in `-> Foo<'c, 'd>`.
|
||||
/// This list is only a list of lifetimes and not type parameters
|
||||
/// because all in-scope type parameters are captured by `impl Trait`,
|
||||
/// so they are resolved directly through the parent `Generics`.
|
||||
TyImplTraitExistential(ExistTy, HirVec<Lifetime>),
|
||||
/// An universally quantified (for all types satisfying) `impl
|
||||
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
|
||||
TyImplTraitUniversal(DefId, TyParamBounds),
|
||||
|
@ -421,8 +421,10 @@ impl<'a> State<'a> {
|
||||
self.print_lifetime(lifetime)?;
|
||||
}
|
||||
}
|
||||
hir::TyImplTraitExistential(ref bounds) |
|
||||
hir::TyImplTraitUniversal(_, ref bounds) => {
|
||||
hir::TyImplTraitExistential(ref existty, ref _lifetimes) => {
|
||||
self.print_bounds("impl", &existty.bounds[..])?;
|
||||
}
|
||||
hir::TyImplTraitUniversal(_, ref bounds) => {
|
||||
self.print_bounds("impl", &bounds[..])?;
|
||||
}
|
||||
hir::TyArray(ref ty, v) => {
|
||||
|
@ -277,6 +277,11 @@ impl_stable_hash_for!(struct hir::BareFnTy {
|
||||
arg_names
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(struct hir::ExistTy {
|
||||
generics,
|
||||
bounds
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(enum hir::Ty_ {
|
||||
TySlice(t),
|
||||
TyArray(t, body_id),
|
||||
@ -287,7 +292,7 @@ impl_stable_hash_for!(enum hir::Ty_ {
|
||||
TyTup(ts),
|
||||
TyPath(qpath),
|
||||
TyTraitObject(trait_refs, lifetime),
|
||||
TyImplTraitExistential(bounds),
|
||||
TyImplTraitExistential(existty, lifetimes),
|
||||
TyImplTraitUniversal(def_id, bounds),
|
||||
TyTypeof(body_id),
|
||||
TyErr,
|
||||
|
@ -84,7 +84,7 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
|
||||
(&ty::ReFree(_), &ty::ReEarlyBound(_)) |
|
||||
(&ty::ReEarlyBound(_), &ty::ReFree(_)) |
|
||||
(&ty::ReFree(_), &ty::ReFree(_)) =>
|
||||
self.free_regions.relation.contains(&sub_region, &super_region),
|
||||
self.free_regions.sub_free_regions(&sub_region, &super_region),
|
||||
|
||||
_ =>
|
||||
false,
|
||||
@ -158,19 +158,39 @@ impl<'tcx> FreeRegionMap<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
|
||||
// (with the exception that `'static: 'x` is not notable)
|
||||
/// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
|
||||
/// (with the exception that `'static: 'x` is not notable)
|
||||
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
|
||||
debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
|
||||
if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) {
|
||||
self.relation.add(sub, sup)
|
||||
}
|
||||
}
|
||||
|
||||
/// True if `r_a <= r_b` is known to hold. Both `r_a` and `r_b`
|
||||
/// must be free regions from the function header.
|
||||
pub fn sub_free_regions<'a, 'gcx>(&self,
|
||||
r_a: Region<'tcx>,
|
||||
r_b: Region<'tcx>)
|
||||
-> bool {
|
||||
debug!("sub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
|
||||
assert!(is_free(r_a));
|
||||
assert!(is_free(r_b));
|
||||
let result = r_a == r_b || self.relation.contains(&r_a, &r_b);
|
||||
debug!("sub_free_regions: result={}", result);
|
||||
result
|
||||
}
|
||||
|
||||
/// Compute the least-upper-bound of two free regions. In some
|
||||
/// cases, this is more conservative than necessary, in order to
|
||||
/// avoid making arbitrary choices. See
|
||||
/// `TransitiveRelation::postdom_upper_bound` for more details.
|
||||
pub fn lub_free_regions<'a, 'gcx>(&self,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
r_a: Region<'tcx>,
|
||||
r_b: Region<'tcx>)
|
||||
-> Region<'tcx> {
|
||||
debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
|
||||
assert!(is_free(r_a));
|
||||
assert!(is_free(r_b));
|
||||
let result = if r_a == r_b { r_a } else {
|
||||
|
@ -52,6 +52,7 @@ impl Region {
|
||||
let i = *index;
|
||||
*index += 1;
|
||||
let def_id = hir_map.local_def_id(def.lifetime.id);
|
||||
debug!("Region::early: index={} def_id={:?}", i, def_id);
|
||||
(def.lifetime.name, Region::EarlyBound(i, def_id))
|
||||
}
|
||||
|
||||
@ -201,6 +202,11 @@ enum Scope<'a> {
|
||||
/// declaration `Binder` and the location it's referenced from.
|
||||
Binder {
|
||||
lifetimes: FxHashMap<hir::LifetimeName, Region>,
|
||||
|
||||
/// if we extend this scope with another scope, what is the next index
|
||||
/// we should use for an early-bound region?
|
||||
next_early_index: u32,
|
||||
|
||||
s: ScopeRef<'a>
|
||||
},
|
||||
|
||||
@ -343,8 +349,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
let lifetimes = generics.lifetimes.iter().map(|def| {
|
||||
Region::early(self.hir_map, &mut index, def)
|
||||
}).collect();
|
||||
let next_early_index = index + generics.ty_params.len() as u32;
|
||||
let scope = Scope::Binder {
|
||||
lifetimes,
|
||||
next_early_index,
|
||||
s: ROOT_SCOPE
|
||||
};
|
||||
self.with(scope, |old_scope, this| {
|
||||
@ -372,12 +380,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
|
||||
debug!("visit_ty: ty={:?}", ty);
|
||||
match ty.node {
|
||||
hir::TyBareFn(ref c) => {
|
||||
let next_early_index = self.next_early_index();
|
||||
let scope = Scope::Binder {
|
||||
lifetimes: c.lifetimes.iter().map(|def| {
|
||||
Region::late(self.hir_map, def)
|
||||
}).collect(),
|
||||
Region::late(self.hir_map, def)
|
||||
}).collect(),
|
||||
next_early_index,
|
||||
s: self.scope
|
||||
};
|
||||
self.with(scope, |old_scope, this| {
|
||||
@ -405,6 +416,60 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
};
|
||||
self.with(scope, |_, this| this.visit_ty(&mt.ty));
|
||||
}
|
||||
hir::TyImplTraitExistential(ref exist_ty, ref lifetimes) => {
|
||||
// Resolve the lifetimes that are applied to the existential type.
|
||||
// These are resolved in the current scope.
|
||||
// `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
|
||||
// `fn foo<'a>() -> MyAnonTy<'a> { ... }`
|
||||
// ^ ^this gets resolved in the current scope
|
||||
for lifetime in lifetimes {
|
||||
self.visit_lifetime(lifetime);
|
||||
|
||||
// Check for predicates like `impl for<'a> SomeTrait<impl OtherTrait<'a>>`
|
||||
// and ban them. Type variables instantiated inside binders aren't
|
||||
// well-supported at the moment, so this doesn't work.
|
||||
// In the future, this should be fixed and this error should be removed.
|
||||
let def = self.map.defs.get(&lifetime.id);
|
||||
if let Some(&Region::LateBound(_, def_id)) = def {
|
||||
if let Some(node_id) = self.hir_map.as_local_node_id(def_id) {
|
||||
// Ensure that the parent of the def is an item, not HRTB
|
||||
let parent_id = self.hir_map.get_parent_node(node_id);
|
||||
let parent_impl_id = hir::ImplItemId { node_id: parent_id };
|
||||
let parent_trait_id = hir::TraitItemId { node_id: parent_id };
|
||||
let krate = self.hir_map.forest.krate();
|
||||
if !(krate.items.contains_key(&parent_id) ||
|
||||
krate.impl_items.contains_key(&parent_impl_id) ||
|
||||
krate.trait_items.contains_key(&parent_trait_id))
|
||||
{
|
||||
span_err!(self.sess, lifetime.span, E0657,
|
||||
"`impl Trait` can only capture lifetimes \
|
||||
bound at the fn or impl level");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the lifetimes in the bounds to the lifetime defs in the generics.
|
||||
// `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
|
||||
// `abstract type MyAnonTy<'b>: MyTrait<'b>;`
|
||||
// ^ ^ this gets resolved in the scope of
|
||||
// the exist_ty generics
|
||||
let hir::ExistTy { ref generics, ref bounds } = *exist_ty;
|
||||
let mut index = self.next_early_index();
|
||||
debug!("visit_ty: index = {}", index);
|
||||
let lifetimes = generics.lifetimes.iter()
|
||||
.map(|lt_def| Region::early(self.hir_map, &mut index, lt_def))
|
||||
.collect();
|
||||
|
||||
let next_early_index = index + generics.ty_params.len() as u32;
|
||||
let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
|
||||
self.with(scope, |_old_scope, this| {
|
||||
this.visit_generics(generics);
|
||||
for bound in bounds {
|
||||
this.visit_ty_param_bound(bound);
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
intravisit::walk_ty(self, ty)
|
||||
}
|
||||
@ -477,10 +542,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
.. }) => {
|
||||
if !bound_lifetimes.is_empty() {
|
||||
self.trait_ref_hack = true;
|
||||
let next_early_index = self.next_early_index();
|
||||
let scope = Scope::Binder {
|
||||
lifetimes: bound_lifetimes.iter().map(|def| {
|
||||
Region::late(self.hir_map, def)
|
||||
}).collect(),
|
||||
}).collect(),
|
||||
next_early_index,
|
||||
s: self.scope
|
||||
};
|
||||
let result = self.with(scope, |old_scope, this| {
|
||||
@ -524,10 +591,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||
span_err!(self.sess, trait_ref.span, E0316,
|
||||
"nested quantification of lifetimes");
|
||||
}
|
||||
let next_early_index = self.next_early_index();
|
||||
let scope = Scope::Binder {
|
||||
lifetimes: trait_ref.bound_lifetimes.iter().map(|def| {
|
||||
Region::late(self.hir_map, def)
|
||||
}).collect(),
|
||||
}).collect(),
|
||||
next_early_index,
|
||||
s: self.scope
|
||||
};
|
||||
self.with(scope, |old_scope, this| {
|
||||
@ -659,7 +728,7 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) {
|
||||
|
||||
Scope::Root => { return; }
|
||||
|
||||
Scope::Binder { ref lifetimes, s } => {
|
||||
Scope::Binder { ref lifetimes, s, next_early_index: _ } => {
|
||||
// FIXME (#24278): non-hygienic comparison
|
||||
if let Some(def) = lifetimes.get(&hir::LifetimeName::Name(label)) {
|
||||
let node_id = hir_map.as_local_node_id(def.id().unwrap())
|
||||
@ -860,8 +929,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let next_early_index = index + generics.ty_params.len() as u32;
|
||||
|
||||
let scope = Scope::Binder {
|
||||
lifetimes,
|
||||
next_early_index,
|
||||
s: self.scope
|
||||
};
|
||||
self.with(scope, move |old_scope, this| {
|
||||
@ -870,7 +942,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the next index one would use for an early-bound-region
|
||||
/// if extending the current scope.
|
||||
fn next_early_index(&self) -> u32 {
|
||||
let mut scope = self.scope;
|
||||
loop {
|
||||
match *scope {
|
||||
Scope::Root =>
|
||||
return 0,
|
||||
|
||||
Scope::Binder { next_early_index, .. } =>
|
||||
return next_early_index,
|
||||
|
||||
Scope::Body { s, .. } |
|
||||
Scope::Elision { s, .. } |
|
||||
Scope::ObjectLifetimeDefault { s, .. } =>
|
||||
scope = s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) {
|
||||
debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref);
|
||||
|
||||
// Walk up the scope chain, tracking the number of fn scopes
|
||||
// that we pass through, until we find a lifetime with the
|
||||
// given name or we run out of scopes.
|
||||
@ -889,7 +983,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
break None;
|
||||
}
|
||||
|
||||
Scope::Binder { ref lifetimes, s } => {
|
||||
Scope::Binder { ref lifetimes, s, next_early_index: _ } => {
|
||||
if let Some(&def) = lifetimes.get(&lifetime_ref.name) {
|
||||
break Some(def.shifted(late_depth));
|
||||
} else {
|
||||
@ -1520,7 +1614,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
Scope::Binder { ref lifetimes, s } => {
|
||||
Scope::Binder { ref lifetimes, s, next_early_index: _ } => {
|
||||
if let Some(&def) = lifetimes.get(&lifetime.name) {
|
||||
let node_id = self.hir_map
|
||||
.as_local_node_id(def.id().unwrap())
|
||||
@ -1549,7 +1643,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
probably a bug in syntax::fold");
|
||||
}
|
||||
|
||||
debug!("{} resolved to {:?} span={:?}",
|
||||
debug!("insert_lifetime: {} resolved to {:?} span={:?}",
|
||||
self.hir_map.node_to_string(lifetime_ref.id),
|
||||
def,
|
||||
self.sess.codemap().span_to_string(lifetime_ref.span));
|
||||
@ -1709,7 +1803,7 @@ fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &hir::Ty) {
|
||||
if let hir::TyImplTraitExistential(_) = ty.node {
|
||||
if let hir::TyImplTraitExistential(..) = ty.node {
|
||||
self.impl_trait = true;
|
||||
}
|
||||
intravisit::walk_ty(self, ty);
|
||||
|
@ -220,11 +220,11 @@ impl<'a, 'gcx, 'tcx> Substs<'tcx> {
|
||||
tcx.intern_substs(&result)
|
||||
}
|
||||
|
||||
fn fill_item<FR, FT>(substs: &mut Vec<Kind<'tcx>>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
defs: &ty::Generics,
|
||||
mk_region: &mut FR,
|
||||
mk_type: &mut FT)
|
||||
pub fn fill_item<FR, FT>(substs: &mut Vec<Kind<'tcx>>,
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
defs: &ty::Generics,
|
||||
mk_region: &mut FR,
|
||||
mk_type: &mut FT)
|
||||
where FR: FnMut(&ty::RegionParameterDef, &[Kind<'tcx>]) -> ty::Region<'tcx>,
|
||||
FT: FnMut(&ty::TypeParameterDef, &[Kind<'tcx>]) -> Ty<'tcx> {
|
||||
|
||||
|
@ -1015,6 +1015,10 @@ define_print! {
|
||||
TyForeign(def_id) => parameterized(f, subst::Substs::empty(), def_id, &[]),
|
||||
TyProjection(ref data) => data.print(f, cx),
|
||||
TyAnon(def_id, substs) => {
|
||||
if cx.is_verbose {
|
||||
return write!(f, "TyAnon({:?}, {:?})", def_id, substs);
|
||||
}
|
||||
|
||||
ty::tls::with(|tcx| {
|
||||
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
|
||||
// by looking up the projections associated with the def_id.
|
||||
|
@ -1487,7 +1487,7 @@ impl<'a, 'b, 'tcx> IndexBuilder<'a, 'b, 'tcx> {
|
||||
|
||||
fn encode_info_for_ty(&mut self, ty: &hir::Ty) {
|
||||
match ty.node {
|
||||
hir::TyImplTraitExistential(_) => {
|
||||
hir::TyImplTraitExistential(..) => {
|
||||
let def_id = self.tcx.hir.local_def_id(ty.id);
|
||||
self.record(def_id, IsolatedEncoder::encode_info_for_anon_ty, def_id);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ use middle::resolve_lifetime as rl;
|
||||
use namespace::Namespace;
|
||||
use rustc::ty::subst::{Kind, Subst, Substs};
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
|
||||
use rustc::ty::{self, RegionKind, Ty, TyCtxt, ToPredicate, TypeFoldable};
|
||||
use rustc::ty::wf::object_region_bounds;
|
||||
use rustc_back::slice;
|
||||
use require_c_abi_if_variadic;
|
||||
@ -1034,9 +1034,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
hir::TyTraitObject(ref bounds, ref lifetime) => {
|
||||
self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime)
|
||||
}
|
||||
hir::TyImplTraitExistential(_) => {
|
||||
hir::TyImplTraitExistential(_, ref lifetimes) => {
|
||||
let def_id = tcx.hir.local_def_id(ast_ty.id);
|
||||
tcx.mk_anon(def_id, Substs::identity_for_item(tcx, def_id))
|
||||
self.impl_trait_ty_to_ty(def_id, lifetimes)
|
||||
}
|
||||
hir::TyImplTraitUniversal(fn_def_id, _) => {
|
||||
let impl_trait_def_id = tcx.hir.local_def_id(ast_ty.id);
|
||||
@ -1097,6 +1097,43 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||
result_ty
|
||||
}
|
||||
|
||||
pub fn impl_trait_ty_to_ty(&self, def_id: DefId, lifetimes: &[hir::Lifetime]) -> Ty<'tcx> {
|
||||
debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes);
|
||||
let tcx = self.tcx();
|
||||
let generics = tcx.generics_of(def_id);
|
||||
|
||||
// Fill in the substs of the parent generics
|
||||
debug!("impl_trait_ty_to_ty: generics={:?}", generics);
|
||||
let mut substs = Vec::with_capacity(generics.count());
|
||||
if let Some(parent_id) = generics.parent {
|
||||
let parent_generics = tcx.generics_of(parent_id);
|
||||
Substs::fill_item(
|
||||
&mut substs, tcx, parent_generics,
|
||||
&mut |def, _| tcx.mk_region(
|
||||
ty::ReEarlyBound(def.to_early_bound_region_data())),
|
||||
&mut |def, _| tcx.mk_param_from_def(def)
|
||||
);
|
||||
|
||||
// Replace all lifetimes with 'static
|
||||
for subst in &mut substs {
|
||||
if let Some(_) = subst.as_region() {
|
||||
*subst = Kind::from(&RegionKind::ReStatic);
|
||||
}
|
||||
}
|
||||
debug!("impl_trait_ty_to_ty: substs from parent = {:?}", substs);
|
||||
}
|
||||
assert_eq!(substs.len(), generics.parent_count());
|
||||
|
||||
// Fill in our own generics with the resolved lifetimes
|
||||
assert_eq!(lifetimes.len(), generics.own_count());
|
||||
substs.extend(lifetimes.iter().map(|lt|
|
||||
Kind::from(self.ast_region_to_region(lt, None))));
|
||||
|
||||
debug!("impl_trait_ty_to_ty: final substs = {:?}", substs);
|
||||
|
||||
tcx.mk_anon(def_id, tcx.intern_substs(&substs))
|
||||
}
|
||||
|
||||
pub fn ty_of_arg(&self,
|
||||
ty: &hir::Ty,
|
||||
expected_ty: Option<Ty<'tcx>>)
|
||||
|
@ -213,7 +213,7 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
// associated fresh inference variable. Writeback resolves these
|
||||
// variables to get the concrete type, which can be used to
|
||||
// deanonymize TyAnon, after typeck is done with all functions.
|
||||
anon_types: RefCell<NodeMap<Ty<'tcx>>>,
|
||||
anon_types: RefCell<DefIdMap<AnonTypeDecl<'tcx>>>,
|
||||
|
||||
/// Each type parameter has an implicit region bound that
|
||||
/// indicates it must outlive at least the function body (the user
|
||||
@ -226,6 +226,43 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
body_id: Option<hir::BodyId>,
|
||||
}
|
||||
|
||||
/// Information about the anonymous, abstract types whose values we
|
||||
/// are inferring in this function (these are the `impl Trait` that
|
||||
/// appear in the return type).
|
||||
#[derive(Debug)]
|
||||
struct AnonTypeDecl<'tcx> {
|
||||
/// The substitutions that we apply to the abstract that that this
|
||||
/// `impl Trait` desugars to. e.g., if:
|
||||
///
|
||||
/// fn foo<'a, 'b, T>() -> impl Trait<'a>
|
||||
///
|
||||
/// winds up desugared to:
|
||||
///
|
||||
/// abstract type Foo<'x, T>: Trait<'x>
|
||||
/// fn foo<'a, 'b, T>() -> Foo<'a, T>
|
||||
///
|
||||
/// then `substs` would be `['a, T]`.
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
|
||||
/// The type variable that represents the value of the abstract type
|
||||
/// that we require. In other words, after we compile this function,
|
||||
/// we will be created a constraint like:
|
||||
///
|
||||
/// Foo<'a, T> = ?C
|
||||
///
|
||||
/// where `?C` is the value of this type variable. =) It may
|
||||
/// naturally refer to the type and lifetime parameters in scope
|
||||
/// in this function, though ultimately it should only reference
|
||||
/// those that are arguments to `Foo` in the constraint above. (In
|
||||
/// other words, `?C` should not include `'b`, even though it's a
|
||||
/// lifetime parameter on `foo`.)
|
||||
concrete_ty: Ty<'tcx>,
|
||||
|
||||
/// A list of all required region bounds on the impl Trait type,
|
||||
/// e.g. `'a` and `'b` in `fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b`.
|
||||
required_region_bounds: Vec<ty::Region<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> {
|
||||
type Target = InferCtxt<'a, 'gcx, 'tcx>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -622,7 +659,7 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
|
||||
deferred_call_resolutions: RefCell::new(DefIdMap()),
|
||||
deferred_cast_checks: RefCell::new(Vec::new()),
|
||||
deferred_generator_interiors: RefCell::new(Vec::new()),
|
||||
anon_types: RefCell::new(NodeMap()),
|
||||
anon_types: RefCell::new(DefIdMap()),
|
||||
implicit_region_bound,
|
||||
body_id,
|
||||
}
|
||||
@ -870,7 +907,10 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
param_env,
|
||||
&fn_sig);
|
||||
|
||||
check_fn(&inh, param_env, fn_sig, decl, id, body, false).0
|
||||
let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, false).0;
|
||||
// Ensure anon_types have been instantiated prior to entering regionck
|
||||
fcx.instantiate_anon_types(&fn_sig.output());
|
||||
fcx
|
||||
} else {
|
||||
let fcx = FnCtxt::new(&inh, param_env, body.value.id);
|
||||
let expected_type = tcx.type_of(def_id);
|
||||
@ -1909,20 +1949,34 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
/// Replace all anonymized types with fresh inference variables
|
||||
/// and record them for writeback.
|
||||
fn instantiate_anon_types<T: TypeFoldable<'tcx>>(&self, value: &T) -> T {
|
||||
debug!("instantiate_anon_types(value={:?})", value);
|
||||
value.fold_with(&mut BottomUpFolder { tcx: self.tcx, fldop: |ty| {
|
||||
if let ty::TyAnon(def_id, substs) = ty.sty {
|
||||
debug!("instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})", def_id, substs);
|
||||
|
||||
// Use the same type variable if the exact same TyAnon appears more
|
||||
// than once in the return type (e.g. if it's passed to a type alias).
|
||||
let id = self.tcx.hir.as_local_node_id(def_id).unwrap();
|
||||
if let Some(ty_var) = self.anon_types.borrow().get(&id) {
|
||||
return ty_var;
|
||||
if let Some(anon_defn) = self.anon_types.borrow().get(&def_id) {
|
||||
return anon_defn.concrete_ty;
|
||||
}
|
||||
let span = self.tcx.def_span(def_id);
|
||||
let ty_var = self.next_ty_var(TypeVariableOrigin::TypeInference(span));
|
||||
self.anon_types.borrow_mut().insert(id, ty_var);
|
||||
|
||||
let predicates_of = self.tcx.predicates_of(def_id);
|
||||
let bounds = predicates_of.instantiate(self.tcx, substs);
|
||||
debug!("instantiate_anon_types: bounds={:?}", bounds);
|
||||
|
||||
let required_region_bounds =
|
||||
self.tcx.required_region_bounds(ty, bounds.predicates.clone());
|
||||
debug!("instantiate_anon_types: required_region_bounds={:?}",
|
||||
required_region_bounds);
|
||||
|
||||
self.anon_types.borrow_mut().insert(def_id, AnonTypeDecl {
|
||||
substs,
|
||||
concrete_ty: ty_var,
|
||||
required_region_bounds,
|
||||
});
|
||||
debug!("instantiate_anon_types: ty_var={:?}", ty_var);
|
||||
|
||||
for predicate in bounds.predicates {
|
||||
// Change the predicate to refer to the type variable,
|
||||
@ -1931,8 +1985,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
let predicate = self.instantiate_anon_types(&predicate);
|
||||
|
||||
// Require that the predicate holds for the concrete type.
|
||||
let cause = traits::ObligationCause::new(span, self.body_id,
|
||||
let cause = traits::ObligationCause::new(span,
|
||||
self.body_id,
|
||||
traits::SizedReturnType);
|
||||
|
||||
debug!("instantiate_anon_types: predicate={:?}", predicate);
|
||||
self.register_predicate(traits::Obligation::new(cause,
|
||||
self.param_env,
|
||||
predicate));
|
||||
|
@ -92,6 +92,7 @@ use rustc::ty::subst::Substs;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::infer::{self, OutlivesEnvironment};
|
||||
use rustc::ty::adjustment;
|
||||
use rustc::ty::outlives::Component;
|
||||
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
@ -135,7 +136,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
item_id: ast::NodeId,
|
||||
span: Span,
|
||||
wf_tys: &[Ty<'tcx>]) {
|
||||
debug!("regionck_item(item.id={:?}, wf_tys={:?}", item_id, wf_tys);
|
||||
debug!("regionck_item(item.id={:?}, wf_tys={:?})", item_id, wf_tys);
|
||||
let subject = self.tcx.hir.local_def_id(item_id);
|
||||
let mut rcx = RegionCtxt::new(self,
|
||||
RepeatingScope(item_id),
|
||||
@ -336,10 +337,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
debug!("visit_fn_body body.id {:?} call_site_scope: {:?}",
|
||||
body.id(), call_site_scope);
|
||||
let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope));
|
||||
|
||||
let body_hir_id = self.tcx.hir.node_to_hir_id(body_id.node_id);
|
||||
self.type_of_node_must_outlive(infer::CallReturn(span),
|
||||
body_hir_id,
|
||||
call_site_region);
|
||||
|
||||
self.constrain_anon_types();
|
||||
}
|
||||
|
||||
fn visit_region_obligations(&mut self, node_id: ast::NodeId)
|
||||
@ -358,6 +362,194 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
self.body_id);
|
||||
}
|
||||
|
||||
/// Go through each of the existential `impl Trait` types that
|
||||
/// appear in the function signature. For example, if the current
|
||||
/// function is as follows:
|
||||
///
|
||||
/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>)
|
||||
///
|
||||
/// we would iterate through the `impl Bar<'a>` and the
|
||||
/// `impl Bar<'b>` here. Remember that each of them has
|
||||
/// their own "abstract type" definition created for them. As
|
||||
/// we iterate, we have a `def_id` that corresponds to this
|
||||
/// definition, and a set of substitutions `substs` that are
|
||||
/// being supplied to this abstract typed definition in the
|
||||
/// signature:
|
||||
///
|
||||
/// abstract type Foo1<'x>: Bar<'x>;
|
||||
/// abstract type Foo2<'x>: Bar<'x>;
|
||||
/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
|
||||
/// ^^^^ ^^ substs
|
||||
/// def_id
|
||||
///
|
||||
/// In addition, for each of the types we will have a type
|
||||
/// variable `concrete_ty` containing the concrete type that
|
||||
/// this function uses for `Foo1` and `Foo2`. That is,
|
||||
/// conceptually, there is a constraint like:
|
||||
///
|
||||
/// for<'a> (Foo1<'a> = C)
|
||||
///
|
||||
/// where `C` is `concrete_ty`. For this equation to be satisfiable,
|
||||
/// the type `C` can only refer to two regions: `'static` and `'a`.
|
||||
///
|
||||
/// The problem is that this type `C` may contain arbitrary
|
||||
/// region variables. In fact, it is fairly likely that it
|
||||
/// does! Consider this possible definition of `foo`:
|
||||
///
|
||||
/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) {
|
||||
/// (&*x, &*y)
|
||||
/// }
|
||||
///
|
||||
/// Here, the values for the concrete types of the two impl
|
||||
/// traits will include inference variables:
|
||||
///
|
||||
/// &'0 i32
|
||||
/// &'1 i32
|
||||
///
|
||||
/// Ordinarily, the subtyping rules would ensure that these are
|
||||
/// sufficiently large. But since `impl Bar<'a>` isn't a specific
|
||||
/// type per se, we don't get such constraints by default. This
|
||||
/// is where this function comes into play. It adds extra
|
||||
/// constraints to ensure that all the regions which appear in the
|
||||
/// inferred type are regions that could validly appear.
|
||||
///
|
||||
/// This is actually a bit of a tricky constraint in general. We
|
||||
/// want to say that each variable (e.g., `'0``) can only take on
|
||||
/// values that were supplied as arguments to the abstract type
|
||||
/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in
|
||||
/// scope. We don't have a constraint quite of this kind in the current
|
||||
/// region checker.
|
||||
///
|
||||
/// What we *do* have is the `<=` relation. So what we do is to
|
||||
/// find the LUB of all the arguments that appear in the substs:
|
||||
/// in this case, that would be `LUB('a) = 'a`, and then we apply
|
||||
/// that as a least bound to the variables (e.g., `'a <= '0`).
|
||||
///
|
||||
/// In some cases this is pretty suboptimal. Consider this example:
|
||||
///
|
||||
/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }
|
||||
///
|
||||
/// Here, the regions `'a` and `'b` appear in the substitutions,
|
||||
/// so we would generate `LUB('a, 'b)` as a kind of "minimal upper
|
||||
/// bound", but that turns out be `'static` -- which is clearly
|
||||
/// too strict!
|
||||
fn constrain_anon_types(&mut self) {
|
||||
debug!("constrain_anon_types()");
|
||||
|
||||
for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() {
|
||||
let concrete_ty = self.resolve_type(anon_defn.concrete_ty);
|
||||
|
||||
debug!("constrain_anon_types: def_id={:?}", def_id);
|
||||
debug!("constrain_anon_types: anon_defn={:#?}", anon_defn);
|
||||
debug!("constrain_anon_types: concrete_ty={:?}", concrete_ty);
|
||||
|
||||
let abstract_type_generics = self.tcx.generics_of(def_id);
|
||||
|
||||
let span = self.tcx.def_span(def_id);
|
||||
|
||||
// If there are required region bounds, we can just skip
|
||||
// ahead. There will already be a registered region
|
||||
// obligation related `concrete_ty` to those regions.
|
||||
if anon_defn.required_region_bounds.len() != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// There were no `required_region_bounds`,
|
||||
// so we have to search for a `least_region`.
|
||||
// Go through all the regions used as arguments to the
|
||||
// abstract type. These are the parameters to the abstract
|
||||
// type; so in our example above, `substs` would contain
|
||||
// `['a]` for the first impl trait and `'b` for the
|
||||
// second.
|
||||
let mut least_region = None;
|
||||
for region_def in &abstract_type_generics.regions {
|
||||
// Find the index of this region in the list of substitutions.
|
||||
let index = region_def.index as usize;
|
||||
|
||||
// Get the value supplied for this region from the substs.
|
||||
let subst_arg = anon_defn.substs[index].as_region().unwrap();
|
||||
|
||||
// Compute the least upper bound of it with the other regions.
|
||||
debug!("constrain_anon_types: least_region={:?}", least_region);
|
||||
debug!("constrain_anon_types: subst_arg={:?}", subst_arg);
|
||||
match least_region {
|
||||
None => least_region = Some(subst_arg),
|
||||
Some(lr) => {
|
||||
if self.outlives_environment
|
||||
.free_region_map()
|
||||
.sub_free_regions(lr, subst_arg) {
|
||||
// keep the current least region
|
||||
} else if self.outlives_environment
|
||||
.free_region_map()
|
||||
.sub_free_regions(subst_arg, lr) {
|
||||
// switch to `subst_arg`
|
||||
least_region = Some(subst_arg);
|
||||
} else {
|
||||
// There are two regions (`lr` and
|
||||
// `subst_arg`) which are not relatable. We can't
|
||||
// find a best choice.
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(span, "ambiguous lifetime bound in `impl Trait`")
|
||||
.span_label(span,
|
||||
format!("neither `{}` nor `{}` outlives the other",
|
||||
lr, subst_arg))
|
||||
.emit();
|
||||
|
||||
least_region = Some(self.tcx.mk_region(ty::ReEmpty));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let least_region = least_region.unwrap_or(self.tcx.types.re_static);
|
||||
debug!("constrain_anon_types: least_region={:?}", least_region);
|
||||
|
||||
// Require that the type `concrete_ty` outlives
|
||||
// `least_region`, modulo any type parameters that appear
|
||||
// in the type, which we ignore. This is because impl
|
||||
// trait values are assumed to capture all the in-scope
|
||||
// type parameters. This little loop here just invokes
|
||||
// `outlives` repeatedly, draining all the nested
|
||||
// obligations that result.
|
||||
let mut types = vec![concrete_ty];
|
||||
let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r);
|
||||
while let Some(ty) = types.pop() {
|
||||
let mut components = self.tcx.outlives_components(ty);
|
||||
while let Some(component) = components.pop() {
|
||||
match component {
|
||||
Component::Region(r) => {
|
||||
bound_region(r);
|
||||
}
|
||||
|
||||
Component::Param(_) => {
|
||||
// ignore type parameters like `T`, they are captured
|
||||
// implicitly by the `impl Trait`
|
||||
}
|
||||
|
||||
Component::UnresolvedInferenceVariable(_) => {
|
||||
// we should get an error that more type
|
||||
// annotations are needed in this case
|
||||
self.tcx.sess.delay_span_bug(span, "unresolved inf var in anon");
|
||||
}
|
||||
|
||||
Component::Projection(ty::ProjectionTy { substs, item_def_id: _ }) => {
|
||||
for r in substs.regions() {
|
||||
bound_region(r);
|
||||
}
|
||||
types.extend(substs.types());
|
||||
}
|
||||
|
||||
Component::EscapingProjection(more_components) => {
|
||||
components.extend(more_components);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_regions_and_report_errors(&self) {
|
||||
self.fcx.resolve_regions_and_report_errors(self.subject_def_id,
|
||||
&self.region_scope_tree,
|
||||
|
@ -18,8 +18,9 @@ use rustc::hir::def_id::{DefId, DefIndex};
|
||||
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
|
||||
use rustc::infer::{InferCtxt};
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::fold::{TypeFolder,TypeFoldable};
|
||||
use rustc::util::nodemap::DefIdSet;
|
||||
use rustc::ty::fold::{TypeFolder, TypeFoldable};
|
||||
use rustc::ty::subst::{Kind, Substs};
|
||||
use rustc::util::nodemap::{DefIdSet, FxHashMap};
|
||||
use syntax::ast;
|
||||
use syntax_pos::Span;
|
||||
use std::mem;
|
||||
@ -285,8 +286,23 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
||||
|
||||
fn visit_anon_types(&mut self) {
|
||||
let gcx = self.tcx().global_tcx();
|
||||
for (&node_id, &concrete_ty) in self.fcx.anon_types.borrow().iter() {
|
||||
let inside_ty = self.resolve(&concrete_ty, &node_id);
|
||||
for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() {
|
||||
let node_id = gcx.hir.as_local_node_id(def_id).unwrap();
|
||||
let inside_ty = self.resolve(&anon_defn.concrete_ty, &node_id);
|
||||
|
||||
// Use substs to build up a reverse map from regions
|
||||
// to their identity mappings.
|
||||
// This is necessary because of `impl Trait` lifetimes
|
||||
// are computed by replacing existing lifetimes with 'static
|
||||
// and remapping only those used in the `impl Trait` return type,
|
||||
// resulting in the parameters shifting.
|
||||
let id_substs = Substs::identity_for_item(gcx, def_id);
|
||||
let map: FxHashMap<Kind<'tcx>, Kind<'gcx>> =
|
||||
anon_defn.substs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, subst)| (*subst, id_substs[index]))
|
||||
.collect();
|
||||
|
||||
// Convert the type from the function into a type valid outside
|
||||
// the function, by replacing invalid regions with 'static,
|
||||
@ -295,25 +311,39 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
||||
match *r {
|
||||
// 'static and early-bound regions are valid.
|
||||
ty::ReStatic |
|
||||
ty::ReEarlyBound(_) |
|
||||
ty::ReEmpty => r,
|
||||
|
||||
ty::ReFree(_) |
|
||||
ty::ReLateBound(..) |
|
||||
ty::ReScope(_) |
|
||||
ty::ReSkolemized(..) => {
|
||||
let span = node_id.to_span(&self.fcx.tcx);
|
||||
span_err!(self.tcx().sess, span, E0564,
|
||||
"only named lifetimes are allowed in `impl Trait`, \
|
||||
but `{}` was found in the type `{}`", r, inside_ty);
|
||||
gcx.types.re_static
|
||||
}
|
||||
|
||||
ty::ReVar(_) |
|
||||
ty::ReErased => {
|
||||
let span = node_id.to_span(&self.fcx.tcx);
|
||||
span_bug!(span, "invalid region in impl Trait: {:?}", 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(r1) =
|
||||
map.get(&Kind::from(r)).and_then(|k| k.as_region()) { 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
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -935,6 +935,10 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
NodeTy(&hir::Ty { node: hir::TyImplTraitExistential(ref exist_ty, _), .. }) => {
|
||||
(&exist_ty.generics, None)
|
||||
}
|
||||
|
||||
_ => (&no_generics, None)
|
||||
};
|
||||
|
||||
@ -1358,6 +1362,8 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
use rustc::hir::map::*;
|
||||
use rustc::hir::*;
|
||||
|
||||
debug!("explicit_predicates_of(def_id={:?})", def_id);
|
||||
|
||||
let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
|
||||
let node = tcx.hir.get(node_id);
|
||||
|
||||
@ -1412,17 +1418,28 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
NodeTy(&Ty { node: TyImplTraitExistential(ref bounds), span, .. }) => {
|
||||
NodeTy(&Ty { node: TyImplTraitExistential(ref exist_ty, _), span, .. }) => {
|
||||
let substs = Substs::identity_for_item(tcx, def_id);
|
||||
let anon_ty = tcx.mk_anon(def_id, substs);
|
||||
|
||||
debug!("explicit_predicates_of: anon_ty={:?}", anon_ty);
|
||||
|
||||
// Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`.
|
||||
let bounds = compute_bounds(&icx, anon_ty, bounds,
|
||||
let bounds = compute_bounds(&icx,
|
||||
anon_ty,
|
||||
&exist_ty.bounds,
|
||||
SizedByDefault::Yes,
|
||||
span);
|
||||
|
||||
debug!("explicit_predicates_of: bounds={:?}", bounds);
|
||||
|
||||
let predicates = bounds.predicates(tcx, anon_ty);
|
||||
|
||||
debug!("explicit_predicates_of: predicates={:?}", predicates);
|
||||
|
||||
return ty::GenericPredicates {
|
||||
parent: None,
|
||||
predicates: bounds.predicates(tcx, anon_ty)
|
||||
predicates: predicates
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1960,9 +1960,8 @@ impl Clean<Type> for hir::Ty {
|
||||
}
|
||||
}
|
||||
TyBareFn(ref barefn) => BareFunction(box barefn.clean(cx)),
|
||||
TyImplTraitExistential(ref bounds) |
|
||||
TyImplTraitUniversal(_, ref bounds) =>
|
||||
ImplTrait(bounds.clean(cx)),
|
||||
TyImplTraitExistential(ref exist_ty, ref _lts) => ImplTrait(exist_ty.bounds.clean(cx)),
|
||||
TyImplTraitUniversal(_, ref bounds) => ImplTrait(bounds.clean(cx)),
|
||||
TyInfer | TyErr => Infer,
|
||||
TyTypeof(..) => panic!("Unimplemented type {:?}", self.node),
|
||||
}
|
||||
|
36
src/test/compile-fail/E0657.rs
Normal file
36
src/test/compile-fail/E0657.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![allow(warnings)]
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
trait Id<T> {}
|
||||
trait Lt<'a> {}
|
||||
|
||||
impl<'a> Lt<'a> for () {}
|
||||
impl<T> Id<T> for T {}
|
||||
|
||||
fn free_fn_capture_hrtb_in_impl_trait()
|
||||
-> impl for<'a> Id<impl Lt<'a>>
|
||||
//~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level [E0657]
|
||||
{
|
||||
()
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn impl_fn_capture_hrtb_in_impl_trait()
|
||||
-> impl for<'a> Id<impl Lt<'a>>
|
||||
//~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level
|
||||
{
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,43 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
// Helper creating a fake borrow, captured by the impl Trait.
|
||||
fn borrow<'a, T>(_: &'a mut T) -> impl Copy { () }
|
||||
|
||||
fn stack() -> impl Copy {
|
||||
//~^ ERROR only named lifetimes are allowed in `impl Trait`
|
||||
let x = 0;
|
||||
&x
|
||||
}
|
||||
|
||||
fn late_bound(x: &i32) -> impl Copy {
|
||||
//~^ ERROR only named lifetimes are allowed in `impl Trait`
|
||||
x
|
||||
}
|
||||
|
||||
// FIXME(#34511) Should work but doesn't at the moment,
|
||||
// region-checking needs an overhault to support this.
|
||||
fn early_bound<'a>(x: &'a i32) -> impl Copy {
|
||||
//~^ ERROR only named lifetimes are allowed in `impl Trait`
|
||||
x
|
||||
}
|
||||
|
||||
fn ambiguous<'a, 'b>(x: &'a [u32], y: &'b [u32]) -> impl Iterator<Item=u32> {
|
||||
//~^ ERROR only named lifetimes are allowed in `impl Trait`
|
||||
if x.len() < y.len() {
|
||||
x.iter().cloned()
|
||||
} else {
|
||||
y.iter().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,39 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
fn elided(x: &i32) -> impl Copy { x }
|
||||
//~^ ERROR cannot infer an appropriate lifetime
|
||||
|
||||
fn explicit<'a>(x: &'a i32) -> impl Copy { x }
|
||||
//~^ ERROR cannot infer an appropriate lifetime
|
||||
|
||||
trait LifetimeTrait<'a> {}
|
||||
impl<'a> LifetimeTrait<'a> for &'a i32 {}
|
||||
|
||||
fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
|
||||
//~^ ERROR cannot infer an appropriate lifetime
|
||||
|
||||
// Tests that a closure type contianing 'b cannot be returned from a type where
|
||||
// only 'a was expected.
|
||||
fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) {
|
||||
//~^ ERROR lifetime mismatch
|
||||
move |_| println!("{}", y)
|
||||
}
|
||||
|
||||
fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,23 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
trait MultiRegionTrait<'a, 'b> {}
|
||||
impl<'a, 'b> MultiRegionTrait<'a, 'b> for (&'a u32, &'b u32) {}
|
||||
|
||||
fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
|
||||
//~^ ERROR ambiguous lifetime bound
|
||||
(x, y)
|
||||
}
|
||||
|
||||
fn main() {}
|
24
src/test/compile-fail/impl-trait/type_parameters_captured.rs
Normal file
24
src/test/compile-fail/impl-trait/type_parameters_captured.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
trait Any {}
|
||||
impl<T> Any for T {}
|
||||
|
||||
// Check that type parameters are captured and not considered 'static
|
||||
fn foo<T>(x: T) -> impl Any + 'static {
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {}
|
97
src/test/run-pass/impl-trait/lifetimes.rs
Normal file
97
src/test/run-pass/impl-trait/lifetimes.rs
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(conservative_impl_trait)]
|
||||
#![allow(warnings)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
fn any_lifetime<'a>() -> &'a u32 { &5 }
|
||||
|
||||
fn static_lifetime() -> &'static u32 { &5 }
|
||||
|
||||
fn any_lifetime_as_static_impl_trait() -> impl Debug {
|
||||
any_lifetime()
|
||||
}
|
||||
|
||||
fn lifetimes_as_static_impl_trait() -> impl Debug {
|
||||
static_lifetime()
|
||||
}
|
||||
|
||||
fn no_params_or_lifetimes_is_static() -> impl Debug + 'static {
|
||||
lifetimes_as_static_impl_trait()
|
||||
}
|
||||
|
||||
fn static_input_type_is_static<T: Debug + 'static>(x: T) -> impl Debug + 'static { x }
|
||||
|
||||
fn type_outlives_reference_lifetime<'a, T: Debug>(x: &'a T) -> impl Debug + 'a { x }
|
||||
|
||||
trait SingleRegionTrait<'a> {}
|
||||
impl<'a> SingleRegionTrait<'a> for u32 {}
|
||||
|
||||
fn simple_type_hrtb<'b>() -> impl for<'a> SingleRegionTrait<'a> { 5 }
|
||||
fn closure_hrtb() -> impl for<'a> Fn(&'a u32) { |_| () }
|
||||
|
||||
fn mixed_lifetimes<'a>() -> impl for<'b: 'a> Fn(&'b u32) { |_| () }
|
||||
fn mixed_as_static() -> impl Fn(&'static u32) { mixed_lifetimes() }
|
||||
|
||||
trait MultiRegionTrait<'a, 'b>: Debug {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MultiRegionStruct<'a, 'b>(&'a u32, &'b u32);
|
||||
impl<'a, 'b> MultiRegionTrait<'a, 'b> for MultiRegionStruct<'a, 'b> {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NoRegionStruct;
|
||||
impl<'a, 'b> MultiRegionTrait<'a, 'b> for NoRegionStruct {}
|
||||
|
||||
fn finds_least_region<'a: 'b, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> {
|
||||
MultiRegionStruct(x, y)
|
||||
}
|
||||
|
||||
fn finds_explicit_bound<'a: 'b, 'b>
|
||||
(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> + 'b
|
||||
{
|
||||
MultiRegionStruct(x, y)
|
||||
}
|
||||
|
||||
fn finds_explicit_bound_even_without_least_region<'a, 'b>
|
||||
(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> + 'b
|
||||
{
|
||||
NoRegionStruct
|
||||
}
|
||||
|
||||
/* FIXME: `impl Trait<'a> + 'b` should live as long as 'b, even if 'b outlives 'a
|
||||
fn outlives_bounds_even_with_contained_regions<'a, 'b>
|
||||
(x: &'a u32, y: &'b u32) -> impl Debug + 'b
|
||||
{
|
||||
finds_explicit_bound_even_without_least_region(x, y)
|
||||
}
|
||||
*/
|
||||
|
||||
fn unnamed_lifetimes_arent_contained_in_impl_trait_and_will_unify<'a, 'b>
|
||||
(x: &'a u32, y: &'b u32) -> impl Debug
|
||||
{
|
||||
fn deref<'lt>(x: &'lt u32) -> impl Debug { *x }
|
||||
|
||||
if true { deref(x) } else { deref(y) }
|
||||
}
|
||||
|
||||
fn can_add_region_bound_to_static_type<'a, 'b>(_: &'a u32) -> impl Debug + 'a { 5 }
|
||||
|
||||
struct MyVec(Vec<Vec<u8>>);
|
||||
|
||||
impl<'unnecessary_lifetime> MyVec {
|
||||
fn iter_doesnt_capture_unnecessary_lifetime<'s>(&'s self) -> impl Iterator<Item = &'s u8> {
|
||||
self.0.iter().flat_map(|inner_vec| inner_vec.iter())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,24 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
// Helper creating a fake borrow, captured by the impl Trait.
|
||||
fn borrow<'a, T>(_: &'a mut T) -> impl Copy { () }
|
||||
|
||||
fn main() {
|
||||
let long;
|
||||
let mut short = 0;
|
||||
long = borrow(&mut short);
|
||||
//~^ NOTE borrow occurs here
|
||||
}
|
||||
//~^ ERROR `short` does not live long enough
|
||||
//~| NOTE `short` dropped here while still borrowed
|
||||
//~| NOTE values in a scope are dropped in the opposite order they are created
|
Loading…
Reference in New Issue
Block a user