1
Fork 0

rustc: move most of lifetime elision to resolve_lifetimes.

This commit is contained in:
Eduard-Mihai Burtescu 2017-01-13 15:09:56 +02:00
parent bbc341424c
commit ba1849daec
13 changed files with 703 additions and 602 deletions

View file

@ -327,6 +327,69 @@ struct ListNode {
This works because `Box` is a pointer, so its size is well-known. This works because `Box` is a pointer, so its size is well-known.
"##, "##,
E0106: r##"
This error indicates that a lifetime is missing from a type. If it is an error
inside a function signature, the problem may be with failing to adhere to the
lifetime elision rules (see below).
Here are some simple examples of where you'll run into this error:
```compile_fail,E0106
struct Foo { x: &bool } // error
struct Foo<'a> { x: &'a bool } // correct
enum Bar { A(u8), B(&bool), } // error
enum Bar<'a> { A(u8), B(&'a bool), } // correct
type MyStr = &str; // error
type MyStr<'a> = &'a str; // correct
```
Lifetime elision is a special, limited kind of inference for lifetimes in
function signatures which allows you to leave out lifetimes in certain cases.
For more background on lifetime elision see [the book][book-le].
The lifetime elision rules require that any function signature with an elided
output lifetime must either have
- exactly one input lifetime
- or, multiple input lifetimes, but the function must also be a method with a
`&self` or `&mut self` receiver
In the first case, the output lifetime is inferred to be the same as the unique
input lifetime. In the second case, the lifetime is instead inferred to be the
same as the lifetime on `&self` or `&mut self`.
Here are some examples of elision errors:
```compile_fail,E0106
// error, no input lifetimes
fn foo() -> &str { }
// error, `x` and `y` have distinct lifetimes inferred
fn bar(x: &str, y: &str) -> &str { }
// error, `y`'s lifetime is inferred to be distinct from `x`'s
fn baz<'a>(x: &'a str, y: &str) -> &str { }
```
Here's an example that is currently an error, but may work in a future version
of Rust:
```compile_fail,E0106
struct Foo<'a>(&'a str);
trait Quux { }
impl Quux for Foo { }
```
Lifetime elision in implementation headers was part of the lifetime elision
RFC. It is, however, [currently unimplemented][iss15872].
[book-le]: https://doc.rust-lang.org/nightly/book/lifetimes.html#lifetime-elision
[iss15872]: https://github.com/rust-lang/rust/issues/15872
"##,
E0109: r##" E0109: r##"
You tried to give a type parameter to a type which doesn't need it. Erroneous You tried to give a type parameter to a type which doesn't need it. Erroneous
code example: code example:

View file

@ -314,9 +314,10 @@ impl<'a> LoweringContext<'a> {
TyKind::Slice(ref ty) => hir::TySlice(self.lower_ty(ty)), TyKind::Slice(ref ty) => hir::TySlice(self.lower_ty(ty)),
TyKind::Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt)), TyKind::Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt)),
TyKind::Rptr(ref region, ref mt) => { TyKind::Rptr(ref region, ref mt) => {
let span = Span { hi: t.span.lo, ..t.span };
let lifetime = match *region { let lifetime = match *region {
Some(ref lt) => self.lower_lifetime(lt), Some(ref lt) => self.lower_lifetime(lt),
None => self.elided_lifetime(t.span) None => self.elided_lifetime(span)
}; };
hir::TyRptr(lifetime, self.lower_mt(mt)) hir::TyRptr(lifetime, self.lower_mt(mt))
} }

View file

@ -22,11 +22,16 @@ use hir::def::Def;
use hir::def_id::DefId; use hir::def_id::DefId;
use middle::region; use middle::region;
use ty; use ty;
use std::cell::Cell;
use std::mem::replace; use std::mem::replace;
use syntax::ast; use syntax::ast;
use syntax::ptr::P;
use syntax::symbol::keywords; use syntax::symbol::keywords;
use syntax_pos::Span; use syntax_pos::Span;
use errors::DiagnosticBuilder;
use util::nodemap::{NodeMap, FxHashSet, FxHashMap}; use util::nodemap::{NodeMap, FxHashSet, FxHashMap};
use rustc_back::slice;
use hir; use hir;
use hir::intravisit::{self, Visitor, NestedVisitorMap}; use hir::intravisit::{self, Visitor, NestedVisitorMap};
@ -36,6 +41,7 @@ pub enum Region {
Static, Static,
EarlyBound(/* index */ u32, /* lifetime decl */ ast::NodeId), EarlyBound(/* index */ u32, /* lifetime decl */ ast::NodeId),
LateBound(ty::DebruijnIndex, /* lifetime decl */ ast::NodeId), LateBound(ty::DebruijnIndex, /* lifetime decl */ ast::NodeId),
LateBoundAnon(ty::DebruijnIndex, /* anon index */ u32),
Free(region::CallSiteScopeData, /* lifetime decl */ ast::NodeId), Free(region::CallSiteScopeData, /* lifetime decl */ ast::NodeId),
} }
@ -51,9 +57,18 @@ impl Region {
(def.lifetime.name, Region::LateBound(depth, def.lifetime.id)) (def.lifetime.name, Region::LateBound(depth, def.lifetime.id))
} }
fn late_anon(index: &Cell<u32>) -> Region {
let i = index.get();
index.set(i + 1);
let depth = ty::DebruijnIndex::new(1);
Region::LateBoundAnon(depth, i)
}
fn id(&self) -> Option<ast::NodeId> { fn id(&self) -> Option<ast::NodeId> {
match *self { match *self {
Region::Static => None, Region::Static |
Region::LateBoundAnon(..) => None,
Region::EarlyBound(_, id) | Region::EarlyBound(_, id) |
Region::LateBound(_, id) | Region::LateBound(_, id) |
Region::Free(_, id) => Some(id) Region::Free(_, id) => Some(id)
@ -65,6 +80,25 @@ impl Region {
Region::LateBound(depth, id) => { Region::LateBound(depth, id) => {
Region::LateBound(depth.shifted(amount), id) Region::LateBound(depth.shifted(amount), id)
} }
Region::LateBoundAnon(depth, index) => {
Region::LateBoundAnon(depth.shifted(amount), index)
}
_ => self
}
}
fn from_depth(self, depth: u32) -> Region {
match self {
Region::LateBound(debruijn, id) => {
Region::LateBound(ty::DebruijnIndex {
depth: debruijn.depth - (depth - 1)
}, id)
}
Region::LateBoundAnon(debruijn, index) => {
Region::LateBoundAnon(ty::DebruijnIndex {
depth: debruijn.depth - (depth - 1)
}, index)
}
_ => self _ => self
} }
} }
@ -122,14 +156,46 @@ enum Scope<'a> {
/// Lifetimes introduced by a fn are scoped to the call-site for that fn, /// Lifetimes introduced by a fn are scoped to the call-site for that fn,
/// if this is a fn body, otherwise the original definitions are used. /// if this is a fn body, otherwise the original definitions are used.
/// Unspecified lifetimes are inferred, unless an elision scope is nested,
/// e.g. `(&T, fn(&T) -> &T);` becomes `(&'_ T, for<'a> fn(&'a T) -> &'a T)`.
Body { Body {
id: hir::BodyId, id: hir::BodyId,
s: ScopeRef<'a> s: ScopeRef<'a>
}, },
/// A scope which either determines unspecified lifetimes or errors
/// on them (e.g. due to ambiguity). For more details, see `Elide`.
Elision {
elide: Elide,
s: ScopeRef<'a>
},
Root Root
} }
#[derive(Clone, Debug)]
enum Elide {
/// Use a fresh anonymous late-bound lifetime each time, by
/// incrementing the counter to generate sequential indices.
FreshLateAnon(Cell<u32>),
/// Always use this one lifetime.
Exact(Region),
/// Like `Exact(Static)` but requires `#![feature(static_in_const)]`.
Static,
/// Less or more than one lifetime were found, error on unspecified.
Error(Vec<ElisionFailureInfo>)
}
#[derive(Clone, Debug)]
struct ElisionFailureInfo {
/// Where we can find the argument pattern.
parent: Option<hir::BodyId>,
/// The index of the argument in the original definition.
index: usize,
lifetime_count: usize,
have_bound_regions: bool
}
type ScopeRef<'a> = &'a Scope<'a>; type ScopeRef<'a> = &'a Scope<'a>;
const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
@ -189,12 +255,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
hir::ItemUse(..) | hir::ItemUse(..) |
hir::ItemMod(..) | hir::ItemMod(..) |
hir::ItemDefaultImpl(..) | hir::ItemDefaultImpl(..) |
hir::ItemForeignMod(..) | hir::ItemForeignMod(..) => {
hir::ItemStatic(..) |
hir::ItemConst(..) => {
// These sorts of items have no lifetime parameters at all. // These sorts of items have no lifetime parameters at all.
intravisit::walk_item(self, item); intravisit::walk_item(self, item);
} }
hir::ItemStatic(..) |
hir::ItemConst(..) => {
// No lifetime parameters, but implied 'static.
let scope = Scope::Elision {
elide: Elide::Static,
s: ROOT_SCOPE
};
self.with(scope, |_, this| intravisit::walk_item(this, item));
}
hir::ItemTy(_, ref generics) | hir::ItemTy(_, ref generics) |
hir::ItemEnum(_, ref generics) | hir::ItemEnum(_, ref generics) |
hir::ItemStruct(_, ref generics) | hir::ItemStruct(_, ref generics) |
@ -299,6 +372,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
if lifetime_ref.is_elided() { if lifetime_ref.is_elided() {
self.resolve_elided_lifetimes(slice::ref_slice(lifetime_ref));
return; return;
} }
if lifetime_ref.name == keywords::StaticLifetime.name() { if lifetime_ref.name == keywords::StaticLifetime.name() {
@ -308,6 +382,31 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
self.resolve_lifetime_ref(lifetime_ref); self.resolve_lifetime_ref(lifetime_ref);
} }
fn visit_path_parameters(&mut self, _: Span, params: &'tcx hir::PathParameters) {
match *params {
hir::AngleBracketedParameters(ref data) => {
if data.lifetimes.iter().all(|l| l.is_elided()) {
self.resolve_elided_lifetimes(&data.lifetimes);
} else {
for l in &data.lifetimes { self.visit_lifetime(l); }
}
for ty in &data.types { self.visit_ty(ty); }
for b in &data.bindings { self.visit_assoc_type_binding(b); }
}
hir::ParenthesizedParameters(ref data) => {
self.visit_fn_like_elision(&data.inputs, data.output.as_ref());
}
}
}
fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl) {
let output = match fd.output {
hir::DefaultReturn(_) => None,
hir::Return(ref ty) => Some(ty)
};
self.visit_fn_like_elision(&fd.inputs, output);
}
fn visit_generics(&mut self, generics: &'tcx hir::Generics) { fn visit_generics(&mut self, generics: &'tcx hir::Generics) {
for ty_param in generics.ty_params.iter() { for ty_param in generics.ty_params.iter() {
walk_list!(self, visit_ty_param_bound, &ty_param.bounds); walk_list!(self, visit_ty_param_bound, &ty_param.bounds);
@ -478,10 +577,6 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) {
} }
intravisit::walk_expr(self, ex) intravisit::walk_expr(self, ex)
} }
fn visit_item(&mut self, _: &hir::Item) {
// do not recurse into items defined in the block
}
} }
fn expression_label(ex: &hir::Expr) -> Option<(ast::Name, Span)> { fn expression_label(ex: &hir::Expr) -> Option<(ast::Name, Span)> {
@ -499,7 +594,9 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) {
label_span: Span) { label_span: Span) {
loop { loop {
match *scope { match *scope {
Scope::Body { s, .. } => { scope = s; } Scope::Body { s, .. } |
Scope::Elision { s, .. } => { scope = s; }
Scope::Root => { return; } Scope::Root => { return; }
Scope::Binder { ref lifetimes, s } => { Scope::Binder { ref lifetimes, s } => {
@ -639,6 +736,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
scope = s; scope = s;
} }
} }
Scope::Elision { s, .. } => {
scope = s;
}
} }
}; };
@ -672,6 +773,386 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
} }
} }
fn visit_fn_like_elision(&mut self, inputs: &'tcx [P<hir::Ty>],
output: Option<&'tcx P<hir::Ty>>) {
let mut arg_elide = Elide::FreshLateAnon(Cell::new(0));
let arg_scope = Scope::Elision {
elide: arg_elide.clone(),
s: self.scope
};
self.with(arg_scope, |_, this| {
for input in inputs {
this.visit_ty(input);
}
match *this.scope {
Scope::Elision { ref elide, .. } => {
arg_elide = elide.clone();
}
_ => bug!()
}
});
let output = match output {
Some(ty) => ty,
None => return
};
// Figure out if there's a body we can get argument names from,
// and whether there's a `self` argument (treated specially).
let mut assoc_item_kind = None;
let mut impl_self = None;
let parent = self.hir_map.get_parent_node(output.id);
let body = match self.hir_map.get(parent) {
// `fn` definitions and methods.
hir::map::NodeItem(&hir::Item {
node: hir::ItemFn(.., body), ..
}) => Some(body),
hir::map::NodeTraitItem(&hir::TraitItem {
node: hir::TraitItemKind::Method(_, ref m), ..
}) => {
match self.hir_map.expect_item(self.hir_map.get_parent(parent)).node {
hir::ItemTrait(.., ref trait_items) => {
assoc_item_kind = trait_items.iter().find(|ti| ti.id.node_id == parent)
.map(|ti| ti.kind);
}
_ => {}
}
match *m {
hir::TraitMethod::Required(_) => None,
hir::TraitMethod::Provided(body) => Some(body),
}
}
hir::map::NodeImplItem(&hir::ImplItem {
node: hir::ImplItemKind::Method(_, body), ..
}) => {
match self.hir_map.expect_item(self.hir_map.get_parent(parent)).node {
hir::ItemImpl(.., ref self_ty, ref impl_items) => {
impl_self = Some(self_ty);
assoc_item_kind = impl_items.iter().find(|ii| ii.id.node_id == parent)
.map(|ii| ii.kind);
}
_ => {}
}
Some(body)
}
// `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) => None,
// Foreign `fn` decls are terrible because we messed up,
// and their return types get argument type elision.
// And now too much code out there is abusing this rule.
hir::map::NodeForeignItem(_) => {
let arg_scope = Scope::Elision {
elide: arg_elide,
s: self.scope
};
self.with(arg_scope, |_, this| this.visit_ty(output));
return;
}
// Everything else (only closures?) doesn't
// actually enjoy elision in return types.
_ => {
self.visit_ty(output);
return;
}
};
let has_self = match assoc_item_kind {
Some(hir::AssociatedItemKind::Method { has_self }) => has_self,
_ => false
};
// In accordance with the rules for lifetime elision, we can determine
// what region to use for elision in the output type in two ways.
// First (determined here), if `self` is by-reference, then the
// implied output region is the region of the self parameter.
if has_self {
// Look for `self: &'a Self` - also desugared from `&'a self`,
// and if that matches, use it for elision and return early.
let is_self_ty = |def: Def| {
if let Def::SelfTy(..) = def {
return true;
}
// Can't always rely on literal (or implied) `Self` due
// to the way elision rules were originally specified.
let impl_self = impl_self.map(|ty| &ty.node);
if let Some(&hir::TyPath(hir::QPath::Resolved(None, ref path))) = impl_self {
match path.def {
// Whitelist the types that unambiguously always
// result in the same type constructor being used
// (it can't differ between `Self` and `self`).
Def::Struct(_) |
Def::Union(_) |
Def::Enum(_) |
Def::Trait(_) |
Def::PrimTy(_) => return def == path.def,
_ => {}
}
}
false
};
if let hir::TyRptr(lifetime_ref, ref mt) = inputs[0].node {
if let hir::TyPath(hir::QPath::Resolved(None, ref path)) = mt.ty.node {
if is_self_ty(path.def) {
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.id) {
let scope = Scope::Elision {
elide: Elide::Exact(lifetime),
s: self.scope
};
self.with(scope, |_, this| this.visit_ty(output));
return;
}
}
}
}
}
// Second, if there was exactly one lifetime (either a substitution or a
// reference) in the arguments, then any anonymous regions in the output
// have that lifetime.
let mut possible_implied_output_region = None;
let mut lifetime_count = 0;
let arg_lifetimes = inputs.iter().enumerate().skip(has_self as usize).map(|(i, input)| {
let mut gather = GatherLifetimes {
map: self.map,
binder_depth: 1,
have_bound_regions: false,
lifetimes: FxHashSet()
};
gather.visit_ty(input);
lifetime_count += gather.lifetimes.len();
if lifetime_count == 1 && gather.lifetimes.len() == 1 {
// there's a chance that the unique lifetime of this
// iteration will be the appropriate lifetime for output
// parameters, so lets store it.
possible_implied_output_region = gather.lifetimes.iter().cloned().next();
}
ElisionFailureInfo {
parent: body,
index: i,
lifetime_count: gather.lifetimes.len(),
have_bound_regions: gather.have_bound_regions
}
}).collect();
let elide = if lifetime_count == 1 {
Elide::Exact(possible_implied_output_region.unwrap())
} else {
Elide::Error(arg_lifetimes)
};
let scope = Scope::Elision {
elide: elide,
s: self.scope
};
self.with(scope, |_, this| this.visit_ty(output));
struct GatherLifetimes<'a> {
map: &'a NamedRegionMap,
binder_depth: u32,
have_bound_regions: bool,
lifetimes: FxHashSet<Region>,
}
impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> {
NestedVisitorMap::None
}
fn visit_ty(&mut self, ty: &hir::Ty) {
let delta = match ty.node {
hir::TyBareFn(_) => 1,
hir::TyPath(hir::QPath::Resolved(None, ref path)) => {
// if this path references a trait, then this will resolve to
// a trait ref, which introduces a binding scope.
match path.def {
Def::Trait(..) => 1,
_ => 0
}
}
_ => 0
};
self.binder_depth += delta;
intravisit::walk_ty(self, ty);
self.binder_depth -= delta;
}
fn visit_poly_trait_ref(&mut self,
trait_ref: &hir::PolyTraitRef,
modifier: &hir::TraitBoundModifier) {
self.binder_depth += 1;
intravisit::walk_poly_trait_ref(self, trait_ref, modifier);
self.binder_depth -= 1;
}
fn visit_lifetime_def(&mut self, lifetime_def: &hir::LifetimeDef) {
for l in &lifetime_def.bounds { self.visit_lifetime(l); }
}
fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.id) {
match lifetime {
Region::LateBound(debruijn, _) |
Region::LateBoundAnon(debruijn, _)
if debruijn.depth < self.binder_depth => {
self.have_bound_regions = true;
}
_ => {
self.lifetimes.insert(lifetime.from_depth(self.binder_depth));
}
}
}
}
}
}
fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[hir::Lifetime]) {
if lifetime_refs.is_empty() {
return;
}
let span = lifetime_refs[0].span;
let mut late_depth = 0;
let mut scope = self.scope;
let error = loop {
match *scope {
// Do not assign any resolution, it will be inferred.
Scope::Body { .. } => return,
Scope::Root => break None,
Scope::Binder { s, .. } => {
late_depth += 1;
scope = s;
}
Scope::Elision { ref elide, .. } => {
let lifetime = match *elide {
Elide::FreshLateAnon(ref counter) => {
for lifetime_ref in lifetime_refs {
let lifetime = Region::late_anon(counter).shifted(late_depth);
self.insert_lifetime(lifetime_ref, lifetime);
}
return;
}
Elide::Exact(l) => l.shifted(late_depth),
Elide::Static => {
if !self.sess.features.borrow().static_in_const {
self.sess
.struct_span_err(span,
"this needs a `'static` lifetime or the \
`static_in_const` feature, see #35897")
.emit();
}
Region::Static
}
Elide::Error(ref e) => break Some(e)
};
for lifetime_ref in lifetime_refs {
self.insert_lifetime(lifetime_ref, lifetime);
}
return;
}
}
};
let mut err = struct_span_err!(self.sess, span, E0106,
"missing lifetime specifier{}",
if lifetime_refs.len() > 1 { "s" } else { "" });
let msg = if lifetime_refs.len() > 1 {
format!("expected {} lifetime parameters", lifetime_refs.len())
} else {
format!("expected lifetime parameter")
};
err.span_label(span, &msg);
if let Some(params) = error {
if lifetime_refs.len() == 1 {
self.report_elision_failure(&mut err, params);
}
}
err.emit();
}
fn report_elision_failure(&mut self,
db: &mut DiagnosticBuilder,
params: &[ElisionFailureInfo]) {
let mut m = String::new();
let len = params.len();
let elided_params: Vec<_> = params.iter().cloned()
.filter(|info| info.lifetime_count > 0)
.collect();
let elided_len = elided_params.len();
for (i, info) in elided_params.into_iter().enumerate() {
let ElisionFailureInfo {
parent, index, lifetime_count: n, have_bound_regions
} = info;
let help_name = if let Some(body) = parent {
let arg = &self.hir_map.body(body).arguments[index];
format!("`{}`", self.hir_map.node_to_pretty_string(arg.pat.id))
} else {
format!("argument {}", index + 1)
};
m.push_str(&(if n == 1 {
help_name
} else {
format!("one of {}'s {} elided {}lifetimes", help_name, n,
if have_bound_regions { "free " } else { "" } )
})[..]);
if elided_len == 2 && i == 0 {
m.push_str(" or ");
} else if i + 2 == elided_len {
m.push_str(", or ");
} else if i != elided_len - 1 {
m.push_str(", ");
}
}
if len == 0 {
help!(db,
"this function's return type contains a borrowed value, but \
there is no value for it to be borrowed from");
help!(db,
"consider giving it a 'static lifetime");
} else if elided_len == 0 {
help!(db,
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments");
help!(db,
"consider giving it an explicit bounded or 'static \
lifetime");
} else if elided_len == 1 {
help!(db,
"this function's return type contains a borrowed value, but \
the signature does not say which {} it is borrowed from",
m);
} else {
help!(db,
"this function's return type contains a borrowed value, but \
the signature does not say whether it is borrowed from {}",
m);
}
}
fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) { fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) {
for i in 0..lifetimes.len() { for i in 0..lifetimes.len() {
let lifetime_i = &lifetimes[i]; let lifetime_i = &lifetimes[i];
@ -729,7 +1210,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
loop { loop {
match *old_scope { match *old_scope {
Scope::Body { s, .. } => { Scope::Body { s, .. } |
Scope::Elision { s, .. } => {
old_scope = s; old_scope = s;
} }

View file

@ -61,9 +61,7 @@ use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable};
use rustc::ty::wf::object_region_bounds; use rustc::ty::wf::object_region_bounds;
use rustc_back::slice; use rustc_back::slice;
use require_c_abi_if_variadic; use require_c_abi_if_variadic;
use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope, use rscope::{RegionScope, ObjectLifetimeDefaultRscope, ShiftedRscope};
ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope,
ElisionFailureInfo, ElidedLifetime};
use rscope::{AnonTypeScope, MaybeWithAnonTypes, ExplicitRscope}; use rscope::{AnonTypeScope, MaybeWithAnonTypes, ExplicitRscope};
use util::common::{ErrorReported, FN_OUTPUT_NAME}; use util::common::{ErrorReported, FN_OUTPUT_NAME};
use util::nodemap::{NodeMap, FxHashSet}; use util::nodemap::{NodeMap, FxHashSet};
@ -74,7 +72,6 @@ use syntax::{abi, ast};
use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::feature_gate::{GateIssue, emit_feature_err};
use syntax::symbol::{Symbol, keywords}; use syntax::symbol::{Symbol, keywords};
use syntax_pos::Span; use syntax_pos::Span;
use errors::DiagnosticBuilder;
pub trait AstConv<'gcx, 'tcx> { pub trait AstConv<'gcx, 'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>; fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
@ -111,6 +108,10 @@ pub trait AstConv<'gcx, 'tcx> {
/// See ParameterEnvironment::free_substs for more information. /// See ParameterEnvironment::free_substs for more information.
fn get_free_substs(&self) -> Option<&Substs<'tcx>>; fn get_free_substs(&self) -> Option<&Substs<'tcx>>;
/// What lifetime should we use when a lifetime is omitted (and not elided)?
fn re_infer(&self, span: Span, _def: Option<&ty::RegionParameterDef>)
-> &'tcx ty::Region;
/// What type should we use when a type is omitted? /// What type should we use when a type is omitted?
fn ty_infer(&self, span: Span) -> Ty<'tcx>; fn ty_infer(&self, span: Span) -> Ty<'tcx>;
@ -161,94 +162,16 @@ struct ConvertedBinding<'tcx> {
/// This type must not appear anywhere in other converted types. /// This type must not appear anywhere in other converted types.
const TRAIT_OBJECT_DUMMY_SELF: ty::TypeVariants<'static> = ty::TyInfer(ty::FreshTy(0)); const TRAIT_OBJECT_DUMMY_SELF: ty::TypeVariants<'static> = ty::TyInfer(ty::FreshTy(0));
fn report_elision_failure(
tcx: TyCtxt,
db: &mut DiagnosticBuilder,
params: Vec<ElisionFailureInfo>)
{
let mut m = String::new();
let len = params.len();
let elided_params: Vec<_> = params.into_iter()
.filter(|info| info.lifetime_count > 0)
.collect();
let elided_len = elided_params.len();
for (i, info) in elided_params.into_iter().enumerate() {
let ElisionFailureInfo {
parent, index, lifetime_count: n, have_bound_regions
} = info;
let help_name = if let Some(body) = parent {
let arg = &tcx.hir.body(body).arguments[index];
format!("`{}`", tcx.hir.node_to_pretty_string(arg.pat.id))
} else {
format!("argument {}", index + 1)
};
m.push_str(&(if n == 1 {
help_name
} else {
format!("one of {}'s {} elided {}lifetimes", help_name, n,
if have_bound_regions { "free " } else { "" } )
})[..]);
if elided_len == 2 && i == 0 {
m.push_str(" or ");
} else if i + 2 == elided_len {
m.push_str(", or ");
} else if i != elided_len - 1 {
m.push_str(", ");
}
}
if len == 0 {
help!(db,
"this function's return type contains a borrowed value, but \
there is no value for it to be borrowed from");
help!(db,
"consider giving it a 'static lifetime");
} else if elided_len == 0 {
help!(db,
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments");
help!(db,
"consider giving it an explicit bounded or 'static \
lifetime");
} else if elided_len == 1 {
help!(db,
"this function's return type contains a borrowed value, but \
the signature does not say which {} it is borrowed from",
m);
} else {
help!(db,
"this function's return type contains a borrowed value, but \
the signature does not say whether it is borrowed from {}",
m);
}
}
impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
pub fn ast_region_to_region(&self, lifetime: &hir::Lifetime) -> &'tcx ty::Region { pub fn ast_region_to_region(&self,
self.opt_ast_region_to_region(&ExplicitRscope, lifetime.span, Some(lifetime), None) lifetime: &hir::Lifetime,
}
fn try_opt_ast_region_to_region(&self,
rscope: &RegionScope,
default_span: Span,
opt_lifetime: Option<&hir::Lifetime>,
def: Option<&ty::RegionParameterDef>) def: Option<&ty::RegionParameterDef>)
-> Result<&'tcx ty::Region, Option<Vec<ElisionFailureInfo>>> -> &'tcx ty::Region
{ {
let tcx = self.tcx(); let tcx = self.tcx();
let name = opt_lifetime.map(|l| l.name); let r = match tcx.named_region_map.defs.get(&lifetime.id) {
let resolved = opt_lifetime.and_then(|l| tcx.named_region_map.defs.get(&l.id));
let r = tcx.mk_region(match resolved {
Some(&rl::Region::Static) => { Some(&rl::Region::Static) => {
ty::ReStatic tcx.mk_region(ty::ReStatic)
} }
Some(&rl::Region::LateBound(debruijn, id)) => { Some(&rl::Region::LateBound(debruijn, id)) => {
@ -263,16 +186,21 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
.get(&id) .get(&id)
.cloned() .cloned()
.unwrap_or(ty::Issue32330::WontChange); .unwrap_or(ty::Issue32330::WontChange);
ty::ReLateBound(debruijn, ty::BrNamed(tcx.hir.local_def_id(id), let name = tcx.hir.name(id);
name.unwrap(), tcx.mk_region(ty::ReLateBound(debruijn,
issue_32330)) ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330)))
} }
Some(&rl::Region::EarlyBound(index, _)) => { Some(&rl::Region::LateBoundAnon(debruijn, index)) => {
ty::ReEarlyBound(ty::EarlyBoundRegion { tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index)))
}
Some(&rl::Region::EarlyBound(index, id)) => {
let name = tcx.hir.name(id);
tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
index: index, index: index,
name: name.unwrap() name: name
}) }))
} }
Some(&rl::Region::Free(scope, id)) => { Some(&rl::Region::Free(scope, id)) => {
@ -283,47 +211,23 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
.get(&id) .get(&id)
.cloned() .cloned()
.unwrap_or(ty::Issue32330::WontChange); .unwrap_or(ty::Issue32330::WontChange);
ty::ReFree(ty::FreeRegion { let name = tcx.hir.name(id);
tcx.mk_region(ty::ReFree(ty::FreeRegion {
scope: scope.to_code_extent(&tcx.region_maps), scope: scope.to_code_extent(&tcx.region_maps),
bound_region: ty::BrNamed(tcx.hir.local_def_id(id), bound_region: ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330)
name.unwrap(), }))
issue_32330)
})
// (*) -- not late-bound, won't change // (*) -- not late-bound, won't change
} }
None => rscope.anon_region(default_span, def)? None => self.re_infer(lifetime.span, def)
}); };
debug!("opt_ast_region_to_region(opt_lifetime={:?}) yields {:?}", debug!("ast_region_to_region(lifetime={:?}) yields {:?}",
opt_lifetime, lifetime,
r); r);
Ok(r) r
}
pub fn opt_ast_region_to_region(&self,
rscope: &RegionScope,
default_span: Span,
opt_lifetime: Option<&hir::Lifetime>,
def: Option<&ty::RegionParameterDef>) -> &'tcx ty::Region
{
let tcx = self.tcx();
self.try_opt_ast_region_to_region(rscope, default_span, opt_lifetime, def)
.unwrap_or_else(|params| {
let ampersand_span = Span { hi: default_span.lo, ..default_span};
let mut err = struct_span_err!(tcx.sess, ampersand_span, E0106,
"missing lifetime specifier");
err.span_label(ampersand_span, &format!("expected lifetime parameter"));
if let Some(params) = params {
report_elision_failure(tcx, &mut err, params);
}
err.emit();
tcx.mk_region(ty::ReStatic)
})
} }
/// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`,
@ -404,20 +308,10 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
}; };
let expected_num_region_params = decl_generics.regions.len(); let expected_num_region_params = decl_generics.regions.len();
let supplied_num_region_params = lifetimes.len(); let supplied_num_region_params = lifetimes.len();
let mut reported_lifetime_count_mismatch = false;
let mut report_lifetime_count_mismatch = || {
if !reported_lifetime_count_mismatch {
reported_lifetime_count_mismatch = true;
let all_infer = lifetimes.iter().all(|lt| lt.is_elided());
let supplied = if all_infer { 0 } else { supplied_num_region_params };
report_lifetime_number_error(tcx, span,
supplied,
expected_num_region_params);
}
};
if expected_num_region_params != supplied_num_region_params { if expected_num_region_params != supplied_num_region_params {
report_lifetime_count_mismatch(); report_lifetime_number_error(tcx, span,
supplied_num_region_params,
expected_num_region_params);
} }
// If a self-type was declared, one should be provided. // If a self-type was declared, one should be provided.
@ -445,11 +339,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
let mut output_assoc_binding = None; let mut output_assoc_binding = None;
let substs = Substs::for_item(tcx, def_id, |def, _| { let substs = Substs::for_item(tcx, def_id, |def, _| {
let i = def.index as usize - self_ty.is_some() as usize; let i = def.index as usize - self_ty.is_some() as usize;
let l = lifetimes.get(i); if let Some(lifetime) = lifetimes.get(i) {
self.try_opt_ast_region_to_region(rscope, span, l, Some(def)).unwrap_or_else(|_| { self.ast_region_to_region(lifetime, Some(def))
report_lifetime_count_mismatch(); } else {
tcx.mk_region(ty::ReStatic) tcx.mk_region(ty::ReStatic)
}) }
}, |def, substs| { }, |def, substs| {
let i = def.index as usize; let i = def.index as usize;
@ -533,72 +427,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
(substs, assoc_bindings) (substs, assoc_bindings)
} }
/// Returns the appropriate lifetime to use for any output lifetimes
/// (if one exists) and a vector of the (pattern, number of lifetimes)
/// corresponding to each input type/pattern.
fn find_implied_output_region<I>(&self,
input_tys: &[Ty<'tcx>],
parent: Option<hir::BodyId>,
input_indices: I) -> ElidedLifetime
where I: Iterator<Item=usize>
{
let tcx = self.tcx();
let mut lifetimes_for_params = Vec::with_capacity(input_tys.len());
let mut possible_implied_output_region = None;
let mut lifetimes = 0;
for (input_type, index) in input_tys.iter().zip(input_indices) {
let mut regions = FxHashSet();
let have_bound_regions = tcx.collect_regions(input_type, &mut regions);
debug!("find_implied_output_regions: collected {:?} from {:?} \
have_bound_regions={:?}", &regions, input_type, have_bound_regions);
lifetimes += regions.len();
if lifetimes == 1 && regions.len() == 1 {
// there's a chance that the unique lifetime of this
// iteration will be the appropriate lifetime for output
// parameters, so lets store it.
possible_implied_output_region = regions.iter().cloned().next();
}
lifetimes_for_params.push(ElisionFailureInfo {
parent: parent,
index: index,
lifetime_count: regions.len(),
have_bound_regions: have_bound_regions
});
}
if lifetimes == 1 {
Ok(*possible_implied_output_region.unwrap())
} else {
Err(Some(lifetimes_for_params))
}
}
fn convert_ty_with_lifetime_elision(&self,
elided_lifetime: ElidedLifetime,
ty: &hir::Ty,
anon_scope: Option<AnonTypeScope>)
-> Ty<'tcx>
{
match elided_lifetime {
Ok(implied_output_region) => {
let rb = ElidableRscope::new(implied_output_region);
self.ast_ty_to_ty(&MaybeWithAnonTypes::new(rb, anon_scope), ty)
}
Err(param_lifetimes) => {
// All regions must be explicitly specified in the output
// if the lifetime elision rules do not apply. This saves
// the user from potentially-confusing errors.
let rb = UnelidableRscope::new(param_lifetimes);
self.ast_ty_to_ty(&MaybeWithAnonTypes::new(rb, anon_scope), ty)
}
}
}
fn convert_parenthesized_parameters(&self, fn convert_parenthesized_parameters(&self,
rscope: &RegionScope, rscope: &RegionScope,
region_substs: &[Kind<'tcx>], region_substs: &[Kind<'tcx>],
@ -606,19 +434,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
-> (Ty<'tcx>, ConvertedBinding<'tcx>) -> (Ty<'tcx>, ConvertedBinding<'tcx>)
{ {
let anon_scope = rscope.anon_type_scope(); let anon_scope = rscope.anon_type_scope();
let binding_rscope = MaybeWithAnonTypes::new(BindingRscope::new(), anon_scope); let rscope = MaybeWithAnonTypes::new(ExplicitRscope, anon_scope);
let inputs = self.tcx().mk_type_list(data.inputs.iter().map(|a_t| { let inputs = self.tcx().mk_type_list(data.inputs.iter().map(|a_t| {
self.ast_ty_arg_to_ty(&binding_rscope, None, region_substs, a_t) self.ast_ty_arg_to_ty(&rscope, None, region_substs, a_t)
})); }));
let input_params = 0..inputs.len();
let implied_output_region = self.find_implied_output_region(&inputs, None, input_params);
let (output, output_span) = match data.output { let (output, output_span) = match data.output {
Some(ref output_ty) => { Some(ref output_ty) => {
(self.convert_ty_with_lifetime_elision(implied_output_region, (self.ast_ty_to_ty(&rscope, output_ty), output_ty.span)
&output_ty,
anon_scope),
output_ty.span)
} }
None => { None => {
(self.tcx().mk_nil(), data.span) (self.tcx().mk_nil(), data.span)
@ -1469,7 +1292,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
}) })
} }
hir::TyRptr(ref region, ref mt) => { hir::TyRptr(ref region, ref mt) => {
let r = self.opt_ast_region_to_region(rscope, ast_ty.span, Some(region), None); let r = self.ast_region_to_region(region, None);
debug!("TyRef r={:?}", r); debug!("TyRef r={:?}", r);
let rscope1 = let rscope1 =
&ObjectLifetimeDefaultRscope::new( &ObjectLifetimeDefaultRscope::new(
@ -1489,9 +1312,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
let anon_scope = rscope.anon_type_scope(); let anon_scope = rscope.anon_type_scope();
let bare_fn_ty = self.ty_of_method_or_bare_fn(bf.unsafety, let bare_fn_ty = self.ty_of_method_or_bare_fn(bf.unsafety,
bf.abi, bf.abi,
None,
&bf.decl, &bf.decl,
None,
anon_scope, anon_scope,
anon_scope); anon_scope);
@ -1626,37 +1447,19 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
} }
} }
pub fn ty_of_method(&self, pub fn ty_of_fn(&self,
sig: &hir::MethodSig,
opt_self_value_ty: Option<Ty<'tcx>>,
body: Option<hir::BodyId>,
anon_scope: Option<AnonTypeScope>)
-> &'tcx ty::BareFnTy<'tcx> {
self.ty_of_method_or_bare_fn(sig.unsafety,
sig.abi,
opt_self_value_ty,
&sig.decl,
body,
None,
anon_scope)
}
pub fn ty_of_bare_fn(&self,
unsafety: hir::Unsafety, unsafety: hir::Unsafety,
abi: abi::Abi, abi: abi::Abi,
decl: &hir::FnDecl, decl: &hir::FnDecl,
body: hir::BodyId,
anon_scope: Option<AnonTypeScope>) anon_scope: Option<AnonTypeScope>)
-> &'tcx ty::BareFnTy<'tcx> { -> &'tcx ty::BareFnTy<'tcx> {
self.ty_of_method_or_bare_fn(unsafety, abi, None, decl, Some(body), None, anon_scope) self.ty_of_method_or_bare_fn(unsafety, abi, decl, None, anon_scope)
} }
fn ty_of_method_or_bare_fn(&self, fn ty_of_method_or_bare_fn(&self,
unsafety: hir::Unsafety, unsafety: hir::Unsafety,
abi: abi::Abi, abi: abi::Abi,
opt_self_value_ty: Option<Ty<'tcx>>,
decl: &hir::FnDecl, decl: &hir::FnDecl,
body: Option<hir::BodyId>,
arg_anon_scope: Option<AnonTypeScope>, arg_anon_scope: Option<AnonTypeScope>,
ret_anon_scope: Option<AnonTypeScope>) ret_anon_scope: Option<AnonTypeScope>)
-> &'tcx ty::BareFnTy<'tcx> -> &'tcx ty::BareFnTy<'tcx>
@ -1665,40 +1468,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
// New region names that appear inside of the arguments of the function // New region names that appear inside of the arguments of the function
// declaration are bound to that function type. // declaration are bound to that function type.
let rb = MaybeWithAnonTypes::new(BindingRscope::new(), arg_anon_scope); let rb = MaybeWithAnonTypes::new(ExplicitRscope, arg_anon_scope);
let input_tys: Vec<Ty> = let input_tys: Vec<Ty> =
decl.inputs.iter().map(|a| self.ty_of_arg(&rb, a, None)).collect(); decl.inputs.iter().map(|a| self.ty_of_arg(&rb, a, None)).collect();
let has_self = opt_self_value_ty.is_some();
let explicit_self = opt_self_value_ty.map(|self_value_ty| {
ExplicitSelf::determine(self_value_ty, input_tys[0])
});
let implied_output_region = match explicit_self {
// `implied_output_region` is the region that will be assumed for any
// region parameters in the return type. In accordance with the rules for
// lifetime elision, we can determine it in two ways. First (determined
// here), if self is by-reference, then the implied output region is the
// region of the self parameter.
Some(ExplicitSelf::ByReference(region, _)) => Ok(*region),
// Second, if there was exactly one lifetime (either a substitution or a
// reference) in the arguments, then any anonymous regions in the output
// have that lifetime.
_ => {
let arg_tys = &input_tys[has_self as usize..];
let arg_params = has_self as usize..input_tys.len();
self.find_implied_output_region(arg_tys, body, arg_params)
}
};
let output_ty = match decl.output { let output_ty = match decl.output {
hir::Return(ref output) => hir::Return(ref output) =>
self.convert_ty_with_lifetime_elision(implied_output_region, self.ast_ty_to_ty(&MaybeWithAnonTypes::new(ExplicitRscope, ret_anon_scope), output),
&output,
ret_anon_scope),
hir::DefaultReturn(..) => self.tcx().mk_nil(), hir::DefaultReturn(..) => self.tcx().mk_nil(),
}; };
@ -1725,10 +1502,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
debug!("ty_of_closure(expected_sig={:?})", debug!("ty_of_closure(expected_sig={:?})",
expected_sig); expected_sig);
// new region names that appear inside of the fn decl are bound to
// that function type
let rb = rscope::BindingRscope::new();
let input_tys = decl.inputs.iter().enumerate().map(|(i, a)| { let input_tys = decl.inputs.iter().enumerate().map(|(i, a)| {
let expected_arg_ty = expected_sig.as_ref().and_then(|e| { let expected_arg_ty = expected_sig.as_ref().and_then(|e| {
// no guarantee that the correct number of expected args // no guarantee that the correct number of expected args
@ -1739,7 +1512,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
None None
} }
}); });
self.ty_of_arg(&rb, a, expected_arg_ty) self.ty_of_arg(&ExplicitRscope, a, expected_arg_ty)
}); });
let expected_ret_ty = expected_sig.as_ref().map(|e| e.output()); let expected_ret_ty = expected_sig.as_ref().map(|e| e.output());
@ -1755,7 +1528,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
expected_ret_ty.unwrap(), expected_ret_ty.unwrap(),
_ if is_infer => self.ty_infer(decl.output.span()), _ if is_infer => self.ty_infer(decl.output.span()),
hir::Return(ref output) => hir::Return(ref output) =>
self.ast_ty_to_ty(&rb, &output), self.ast_ty_to_ty(&ExplicitRscope, &output),
hir::DefaultReturn(..) => bug!(), hir::DefaultReturn(..) => bug!(),
}; };
@ -1820,7 +1593,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
if let Some(&r) = explicit_region_bounds.get(0) { if let Some(&r) = explicit_region_bounds.get(0) {
// Explicitly specified region bound. Use that. // Explicitly specified region bound. Use that.
return Some(self.ast_region_to_region(r)); return Some(self.ast_region_to_region(r, None));
} }
if let Some(principal) = existential_predicates.principal() { if let Some(principal) = existential_predicates.principal() {

View file

@ -97,7 +97,7 @@ use rustc::ty::adjustment;
use rustc::ty::fold::{BottomUpFolder, TypeFoldable}; use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
use rustc::ty::util::{Representability, IntTypeExt}; use rustc::ty::util::{Representability, IntTypeExt};
use require_c_abi_if_variadic; use require_c_abi_if_variadic;
use rscope::{ElisionFailureInfo, RegionScope}; use rscope::RegionScope;
use session::{Session, CompileResult}; use session::{Session, CompileResult};
use CrateCtxt; use CrateCtxt;
use TypeAndSubsts; use TypeAndSubsts;
@ -1410,6 +1410,15 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> {
Ok(r) Ok(r)
} }
fn re_infer(&self, span: Span, def: Option<&ty::RegionParameterDef>)
-> &'tcx ty::Region {
let v = match def {
Some(def) => infer::EarlyBoundRegion(span, def.name),
None => infer::MiscVariable(span)
};
self.next_region_var(v)
}
fn ty_infer(&self, span: Span) -> Ty<'tcx> { fn ty_infer(&self, span: Span) -> Ty<'tcx> {
self.next_ty_var(TypeVariableOrigin::TypeInference(span)) self.next_ty_var(TypeVariableOrigin::TypeInference(span))
} }
@ -1465,15 +1474,6 @@ impl<'a, 'gcx, 'tcx> RegionScope for FnCtxt<'a, 'gcx, 'tcx> {
// be some expression). // be some expression).
*self.next_region_var(infer::MiscVariable(span)) *self.next_region_var(infer::MiscVariable(span))
} }
fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>> {
let v = match def {
Some(def) => infer::EarlyBoundRegion(span, def.name),
None => infer::MiscVariable(span)
};
Ok(*self.next_region_var(v))
}
} }
/// Controls whether the arguments are tupled. This is used for the call /// Controls whether the arguments are tupled. This is used for the call
@ -4408,7 +4408,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
None => &[] None => &[]
}; };
AstConv::opt_ast_region_to_region(self, self, span, lifetimes.get(i), Some(def)) if let Some(lifetime) = lifetimes.get(i) {
AstConv::ast_region_to_region(self, lifetime, Some(def))
} else {
self.re_infer(span, Some(def))
}
}, |def, substs| { }, |def, substs| {
let mut i = def.index as usize; let mut i = def.index as usize;

View file

@ -437,6 +437,11 @@ impl<'a, 'tcx> AstConv<'tcx, 'tcx> for ItemCtxt<'a, 'tcx> {
None None
} }
fn re_infer(&self, span: Span, _def: Option<&ty::RegionParameterDef>)
-> &'tcx ty::Region {
span_bug!(span, "unelided lifetime in signature");
}
fn ty_infer(&self, span: Span) -> Ty<'tcx> { fn ty_infer(&self, span: Span) -> Ty<'tcx> {
struct_span_err!( struct_span_err!(
self.tcx().sess, self.tcx().sess,
@ -639,8 +644,6 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
container: AssociatedItemContainer, container: AssociatedItemContainer,
id: ast::NodeId, id: ast::NodeId,
sig: &hir::MethodSig, sig: &hir::MethodSig,
untransformed_rcvr_ty: Ty<'tcx>,
body: Option<hir::BodyId>,
rcvr_ty_predicates: &ty::GenericPredicates<'tcx>,) { rcvr_ty_predicates: &ty::GenericPredicates<'tcx>,) {
let def_id = ccx.tcx.hir.local_def_id(id); let def_id = ccx.tcx.hir.local_def_id(id);
let ty_generics = generics_of_def_id(ccx, def_id); let ty_generics = generics_of_def_id(ccx, def_id);
@ -652,14 +655,8 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
ImplContainer(_) => Some(AnonTypeScope::new(def_id)), ImplContainer(_) => Some(AnonTypeScope::new(def_id)),
TraitContainer(_) => None TraitContainer(_) => None
}; };
let assoc_item = ccx.tcx.associated_item(def_id); let fty = AstConv::ty_of_fn(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)),
let self_value_ty = if assoc_item.method_has_self_argument { sig.unsafety, sig.abi, &sig.decl, anon_scope);
Some(untransformed_rcvr_ty)
} else {
None
};
let fty = AstConv::ty_of_method(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)),
sig, self_value_ty, body, anon_scope);
let substs = mk_item_substs(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)), let substs = mk_item_substs(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)),
ccx.tcx.hir.span(id), def_id); ccx.tcx.hir.span(id), def_id);
@ -876,14 +873,9 @@ fn convert_trait_item(ccx: &CrateCtxt, trait_item: &hir::TraitItem) {
convert_associated_type(ccx, TraitContainer(trait_def_id), trait_item.id, typ); convert_associated_type(ccx, TraitContainer(trait_def_id), trait_item.id, typ);
} }
hir::TraitItemKind::Method(ref sig, ref method) => { hir::TraitItemKind::Method(ref sig, _) => {
let body = match *method {
hir::TraitMethod::Required(_) => None,
hir::TraitMethod::Provided(body) => Some(body)
};
convert_method(ccx, TraitContainer(trait_def_id), convert_method(ccx, TraitContainer(trait_def_id),
trait_item.id, sig, tcx.mk_self_type(), trait_item.id, sig, &trait_predicates);
body, &trait_predicates);
} }
} }
} }
@ -896,7 +888,6 @@ fn convert_impl_item(ccx: &CrateCtxt, impl_item: &hir::ImplItem) {
let impl_def_id = tcx.hir.get_parent_did(impl_item.id); let impl_def_id = tcx.hir.get_parent_did(impl_item.id);
let impl_predicates = tcx.item_predicates(impl_def_id); let impl_predicates = tcx.item_predicates(impl_def_id);
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
let impl_self_ty = tcx.item_type(impl_def_id);
match impl_item.node { match impl_item.node {
hir::ImplItemKind::Const(ref ty, _) => { hir::ImplItemKind::Const(ref ty, _) => {
@ -923,10 +914,8 @@ fn convert_impl_item(ccx: &CrateCtxt, impl_item: &hir::ImplItem) {
convert_associated_type(ccx, ImplContainer(impl_def_id), impl_item.id, Some(typ)); convert_associated_type(ccx, ImplContainer(impl_def_id), impl_item.id, Some(typ));
} }
hir::ImplItemKind::Method(ref sig, body) => { hir::ImplItemKind::Method(ref sig, _) => {
convert_method(ccx, ImplContainer(impl_def_id), convert_method(ccx, ImplContainer(impl_def_id), impl_item.id, sig, &impl_predicates);
impl_item.id, sig, impl_self_ty,
Some(body), &impl_predicates);
} }
} }
} }
@ -1472,7 +1461,7 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
index: own_start + i as u32, index: own_start + i as u32,
def_id: tcx.hir.local_def_id(l.lifetime.id), def_id: tcx.hir.local_def_id(l.lifetime.id),
bounds: l.bounds.iter().map(|l| { bounds: l.bounds.iter().map(|l| {
AstConv::ast_region_to_region(&ccx.icx(&()), l) AstConv::ast_region_to_region(&ccx.icx(&()), l, None)
}).collect(), }).collect(),
pure_wrt_drop: l.pure_wrt_drop, pure_wrt_drop: l.pure_wrt_drop,
} }
@ -1545,11 +1534,11 @@ fn type_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
NodeItem(item) => { NodeItem(item) => {
match item.node { match item.node {
ItemStatic(ref t, ..) | ItemConst(ref t, _) => { ItemStatic(ref t, ..) | ItemConst(ref t, _) => {
ccx.icx(&()).to_ty(&StaticRscope::new(&ccx.tcx), &t) ccx.icx(&()).to_ty(&ExplicitRscope, &t)
} }
ItemFn(ref decl, unsafety, _, abi, ref generics, body) => { ItemFn(ref decl, unsafety, _, abi, ref generics, _) => {
let tofd = AstConv::ty_of_bare_fn(&ccx.icx(generics), unsafety, abi, &decl, let tofd = AstConv::ty_of_fn(&ccx.icx(generics), unsafety, abi, &decl,
body, Some(AnonTypeScope::new(def_id))); Some(AnonTypeScope::new(def_id)));
let substs = mk_item_substs(&ccx.icx(generics), item.span, def_id); let substs = mk_item_substs(&ccx.icx(generics), item.span, def_id);
ccx.tcx.mk_fn_def(def_id, substs, tofd) ccx.tcx.mk_fn_def(def_id, substs, tofd)
} }
@ -1765,7 +1754,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
name: param.lifetime.name name: param.lifetime.name
})); }));
for bound in &param.bounds { for bound in &param.bounds {
let bound_region = AstConv::ast_region_to_region(&ccx.icx(&()), bound); let bound_region = AstConv::ast_region_to_region(&ccx.icx(&()), bound, None);
let outlives = ty::Binder(ty::OutlivesPredicate(region, bound_region)); let outlives = ty::Binder(ty::OutlivesPredicate(region, bound_region));
predicates.push(outlives.to_predicate()); predicates.push(outlives.to_predicate());
} }
@ -1816,7 +1805,9 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
} }
&hir::TyParamBound::RegionTyParamBound(ref lifetime) => { &hir::TyParamBound::RegionTyParamBound(ref lifetime) => {
let region = AstConv::ast_region_to_region(&ccx.icx(&()), lifetime); let region = AstConv::ast_region_to_region(&ccx.icx(&()),
lifetime,
None);
let pred = ty::Binder(ty::OutlivesPredicate(ty, region)); let pred = ty::Binder(ty::OutlivesPredicate(ty, region));
predicates.push(ty::Predicate::TypeOutlives(pred)) predicates.push(ty::Predicate::TypeOutlives(pred))
} }
@ -1825,9 +1816,9 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
} }
&hir::WherePredicate::RegionPredicate(ref region_pred) => { &hir::WherePredicate::RegionPredicate(ref region_pred) => {
let r1 = AstConv::ast_region_to_region(&ccx.icx(&()), &region_pred.lifetime); let r1 = AstConv::ast_region_to_region(&ccx.icx(&()), &region_pred.lifetime, None);
for bound in &region_pred.bounds { for bound in &region_pred.bounds {
let r2 = AstConv::ast_region_to_region(&ccx.icx(&()), bound); let r2 = AstConv::ast_region_to_region(&ccx.icx(&()), bound, None);
let pred = ty::Binder(ty::OutlivesPredicate(r1, r2)); let pred = ty::Binder(ty::OutlivesPredicate(r1, r2));
predicates.push(ty::Predicate::RegionOutlives(pred)) predicates.push(ty::Predicate::RegionOutlives(pred))
} }
@ -1935,7 +1926,7 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
hir::TraitTyParamBound(..) => hir::TraitTyParamBound(..) =>
None, None,
hir::RegionTyParamBound(ref lifetime) => hir::RegionTyParamBound(ref lifetime) =>
Some(AstConv::ast_region_to_region(&ccx.icx(&()), lifetime)), Some(AstConv::ast_region_to_region(&ccx.icx(&()), lifetime, None)),
} }
}) })
.collect() .collect()
@ -1997,7 +1988,7 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>,
}).collect(); }).collect();
let region_bounds = region_bounds.into_iter().map(|r| { let region_bounds = region_bounds.into_iter().map(|r| {
astconv.ast_region_to_region(r) astconv.ast_region_to_region(r, None)
}).collect(); }).collect();
trait_bounds.sort_by(|a,b| a.def_id().cmp(&b.def_id())); trait_bounds.sort_by(|a,b| a.def_id().cmp(&b.def_id()));
@ -2039,7 +2030,7 @@ fn predicates_from_bound<'tcx>(astconv: &AstConv<'tcx, 'tcx>,
.collect() .collect()
} }
hir::RegionTyParamBound(ref lifetime) => { hir::RegionTyParamBound(ref lifetime) => {
let region = astconv.ast_region_to_region(lifetime); let region = astconv.ast_region_to_region(lifetime, None);
let pred = ty::Binder(ty::OutlivesPredicate(param_ty, region)); let pred = ty::Binder(ty::OutlivesPredicate(param_ty, region));
vec![ty::Predicate::TypeOutlives(pred)] vec![ty::Predicate::TypeOutlives(pred)]
} }
@ -2057,18 +2048,7 @@ fn compute_type_of_foreign_fn_decl<'a, 'tcx>(
abi: abi::Abi) abi: abi::Abi)
-> Ty<'tcx> -> Ty<'tcx>
{ {
let rb = BindingRscope::new(); let fty = AstConv::ty_of_fn(&ccx.icx(ast_generics), hir::Unsafety::Unsafe, abi, decl, None);
let input_tys = decl.inputs
.iter()
.map(|a| AstConv::ty_of_arg(&ccx.icx(ast_generics), &rb, a, None))
.collect::<Vec<_>>();
let output = match decl.output {
hir::Return(ref ty) =>
AstConv::ast_ty_to_ty(&ccx.icx(ast_generics), &rb, &ty),
hir::DefaultReturn(..) =>
ccx.tcx.mk_nil(),
};
// feature gate SIMD types in FFI, since I (huonw) am not sure the // feature gate SIMD types in FFI, since I (huonw) am not sure the
// ABIs are handled at all correctly. // ABIs are handled at all correctly.
@ -2084,21 +2064,17 @@ fn compute_type_of_foreign_fn_decl<'a, 'tcx>(
.emit(); .emit();
} }
}; };
for (input, ty) in decl.inputs.iter().zip(&input_tys) { for (input, ty) in decl.inputs.iter().zip(*fty.sig.inputs().skip_binder()) {
check(&input, ty) check(&input, ty)
} }
if let hir::Return(ref ty) = decl.output { if let hir::Return(ref ty) = decl.output {
check(&ty, output) check(&ty, *fty.sig.output().skip_binder())
} }
} }
let id = ccx.tcx.hir.as_local_node_id(def_id).unwrap(); let id = ccx.tcx.hir.as_local_node_id(def_id).unwrap();
let substs = mk_item_substs(&ccx.icx(ast_generics), ccx.tcx.hir.span(id), def_id); let substs = mk_item_substs(&ccx.icx(ast_generics), ccx.tcx.hir.span(id), def_id);
ccx.tcx.mk_fn_def(def_id, substs, ccx.tcx.mk_bare_fn(ty::BareFnTy { ccx.tcx.mk_fn_def(def_id, substs, fty)
abi: abi,
unsafety: hir::Unsafety::Unsafe,
sig: ty::Binder(ccx.tcx.mk_fn_sig(input_tys.into_iter(), output, decl.variadic)),
}))
} }
pub fn mk_item_substs<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, pub fn mk_item_substs<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>,

View file

@ -1412,85 +1412,19 @@ fn main() {
``` ```
"##, "##,
E0106: r##"
This error indicates that a lifetime is missing from a type. If it is an error
inside a function signature, the problem may be with failing to adhere to the
lifetime elision rules (see below).
Here are some simple examples of where you'll run into this error:
```compile_fail,E0106
struct Foo { x: &bool } // error
struct Foo<'a> { x: &'a bool } // correct
enum Bar { A(u8), B(&bool), } // error
enum Bar<'a> { A(u8), B(&'a bool), } // correct
type MyStr = &str; // error
type MyStr<'a> = &'a str; // correct
```
Lifetime elision is a special, limited kind of inference for lifetimes in
function signatures which allows you to leave out lifetimes in certain cases.
For more background on lifetime elision see [the book][book-le].
The lifetime elision rules require that any function signature with an elided
output lifetime must either have
- exactly one input lifetime
- or, multiple input lifetimes, but the function must also be a method with a
`&self` or `&mut self` receiver
In the first case, the output lifetime is inferred to be the same as the unique
input lifetime. In the second case, the lifetime is instead inferred to be the
same as the lifetime on `&self` or `&mut self`.
Here are some examples of elision errors:
```compile_fail,E0106
// error, no input lifetimes
fn foo() -> &str { }
// error, `x` and `y` have distinct lifetimes inferred
fn bar(x: &str, y: &str) -> &str { }
// error, `y`'s lifetime is inferred to be distinct from `x`'s
fn baz<'a>(x: &'a str, y: &str) -> &str { }
```
[book-le]: https://doc.rust-lang.org/nightly/book/lifetimes.html#lifetime-elision
"##,
E0107: r##" E0107: r##"
This error means that an incorrect number of lifetime parameters were provided This error means that an incorrect number of lifetime parameters were provided
for a type (like a struct or enum) or trait. for a type (like a struct or enum) or trait:
Some basic examples include:
```compile_fail,E0107 ```compile_fail,E0107
struct Foo<'a>(&'a str); struct Foo<'a, 'b>(&'a str, &'b str);
enum Bar { A, B, C } enum Bar { A, B, C }
struct Baz<'a> { struct Baz<'a> {
foo: Foo, // error: expected 1, found 0 foo: Foo<'a>, // error: expected 2, found 1
bar: Bar<'a>, // error: expected 0, found 1 bar: Bar<'a>, // error: expected 0, found 1
} }
``` ```
Here's an example that is currently an error, but may work in a future version
of Rust:
```compile_fail,E0107
struct Foo<'a>(&'a str);
trait Quux { }
impl Quux for Foo { } // error: expected 1, found 0
```
Lifetime elision in implementation headers was part of the lifetime elision
RFC. It is, however, [currently unimplemented][iss15872].
[iss15872]: https://github.com/rust-lang/rust/issues/15872
"##, "##,
E0116: r##" E0116: r##"

View file

@ -8,28 +8,14 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use rustc::hir;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::ty; use rustc::ty;
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use astconv::AstConv; use astconv::AstConv;
use std::cell::Cell;
use syntax_pos::Span; use syntax_pos::Span;
#[derive(Clone)]
pub struct ElisionFailureInfo {
/// Where we can find the argument pattern.
pub parent: Option<hir::BodyId>,
/// The index of the argument in the original definition.
pub index: usize,
pub lifetime_count: usize,
pub have_bound_regions: bool
}
pub type ElidedLifetime = Result<ty::Region, Option<Vec<ElisionFailureInfo>>>;
/// Defines strategies for handling regions that are omitted. For /// Defines strategies for handling regions that are omitted. For
/// example, if one writes the type `&Foo`, then the lifetime of /// example, if one writes the type `&Foo`, then the lifetime of
/// this reference has been omitted. When converting this /// this reference has been omitted. When converting this
@ -41,9 +27,6 @@ pub type ElidedLifetime = Result<ty::Region, Option<Vec<ElisionFailureInfo>>>;
/// can return `Err(())` to indicate that this is not a scope in which /// can return `Err(())` to indicate that this is not a scope in which
/// regions can legally be omitted. /// regions can legally be omitted.
pub trait RegionScope { pub trait RegionScope {
fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>>;
/// If an object omits any explicit lifetime bound, and none can /// If an object omits any explicit lifetime bound, and none can
/// be derived from the object traits, what should we use? If /// be derived from the object traits, what should we use? If
/// `None` is returned, an explicit annotation is required. /// `None` is returned, an explicit annotation is required.
@ -115,11 +98,6 @@ impl<R: RegionScope> RegionScope for MaybeWithAnonTypes<R> {
self.base_scope.object_lifetime_default(span) self.base_scope.object_lifetime_default(span)
} }
fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>> {
self.base_scope.anon_region(span, def)
}
fn base_object_lifetime_default(&self, span: Span) -> ty::Region { fn base_object_lifetime_default(&self, span: Span) -> ty::Region {
self.base_scope.base_object_lifetime_default(span) self.base_scope.base_object_lifetime_default(span)
} }
@ -135,11 +113,6 @@ impl<R: RegionScope> RegionScope for MaybeWithAnonTypes<R> {
pub struct ExplicitRscope; pub struct ExplicitRscope;
impl RegionScope for ExplicitRscope { impl RegionScope for ExplicitRscope {
fn anon_region(&self, _span: Span, _: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>> {
Err(None)
}
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> { fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
Some(self.base_object_lifetime_default(span)) Some(self.base_object_lifetime_default(span))
} }
@ -149,135 +122,6 @@ impl RegionScope for ExplicitRscope {
} }
} }
// Same as `ExplicitRscope`, but provides some extra information for diagnostics
pub struct UnelidableRscope(Option<Vec<ElisionFailureInfo>>);
impl UnelidableRscope {
pub fn new(v: Option<Vec<ElisionFailureInfo>>) -> UnelidableRscope {
UnelidableRscope(v)
}
}
impl RegionScope for UnelidableRscope {
fn anon_region(&self, _span: Span, _: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>> {
Err(self.0.clone())
}
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
Some(self.base_object_lifetime_default(span))
}
fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
ty::ReStatic
}
}
// A scope in which omitted anonymous region defaults to
// `default`. This is used after the `->` in function signatures. The
// latter use may go away. Note that object-lifetime defaults work a
// bit differently, as specified in RFC #599.
pub struct ElidableRscope {
default: ty::Region,
}
impl ElidableRscope {
pub fn new(r: ty::Region) -> ElidableRscope {
ElidableRscope { default: r }
}
}
impl RegionScope for ElidableRscope {
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
// Per RFC #599, object-lifetimes default to 'static unless
// overridden by context, and this takes precedence over
// lifetime elision.
Some(self.base_object_lifetime_default(span))
}
fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
ty::ReStatic
}
fn anon_region(&self, _span: Span, _: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>>
{
Ok(self.default)
}
}
/// A scope that behaves as an ElidabeRscope with a `'static` default region
/// that should also warn if the `static_in_const` feature is unset.
#[derive(Copy, Clone)]
pub struct StaticRscope<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
tcx: &'a ty::TyCtxt<'a, 'gcx, 'tcx>,
}
impl<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> StaticRscope<'a, 'gcx, 'tcx> {
/// create a new StaticRscope from a reference to the `TyCtxt`
pub fn new(tcx: &'a ty::TyCtxt<'a, 'gcx, 'tcx>) -> Self {
StaticRscope { tcx: tcx }
}
}
impl<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> RegionScope for StaticRscope<'a, 'gcx, 'tcx> {
fn anon_region(&self, span: Span, _: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>> {
if !self.tcx.sess.features.borrow().static_in_const {
self.tcx
.sess
.struct_span_err(span,
"this needs a `'static` lifetime or the \
`static_in_const` feature, see #35897")
.emit();
}
Ok(ty::ReStatic)
}
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
Some(self.base_object_lifetime_default(span))
}
fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
ty::ReStatic
}
}
/// A scope in which we generate anonymous, late-bound regions for
/// omitted regions. This occurs in function signatures.
pub struct BindingRscope {
anon_bindings: Cell<u32>,
}
impl BindingRscope {
pub fn new() -> BindingRscope {
BindingRscope {
anon_bindings: Cell::new(0),
}
}
}
impl RegionScope for BindingRscope {
fn object_lifetime_default(&self, span: Span) -> Option<ty::Region> {
// Per RFC #599, object-lifetimes default to 'static unless
// overridden by context, and this takes precedence over the
// binding defaults in a fn signature.
Some(self.base_object_lifetime_default(span))
}
fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
ty::ReStatic
}
fn anon_region(&self, _: Span, _: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>>
{
let idx = self.anon_bindings.get();
self.anon_bindings.set(idx + 1);
Ok(ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(idx)))
}
}
/// A scope which overrides the default object lifetime but has no other effect. /// A scope which overrides the default object lifetime but has no other effect.
pub struct ObjectLifetimeDefaultRscope<'r> { pub struct ObjectLifetimeDefaultRscope<'r> {
base_scope: &'r (RegionScope+'r), base_scope: &'r (RegionScope+'r),
@ -315,12 +159,6 @@ impl<'r> RegionScope for ObjectLifetimeDefaultRscope<'r> {
self.base_scope.base_object_lifetime_default(span) self.base_scope.base_object_lifetime_default(span)
} }
fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>>
{
self.base_scope.anon_region(span, def)
}
fn anon_type_scope(&self) -> Option<AnonTypeScope> { fn anon_type_scope(&self) -> Option<AnonTypeScope> {
self.base_scope.anon_type_scope() self.base_scope.anon_type_scope()
} }
@ -348,12 +186,6 @@ impl<'r> RegionScope for ShiftedRscope<'r> {
ty::fold::shift_region(self.base_scope.base_object_lifetime_default(span), 1) ty::fold::shift_region(self.base_scope.base_object_lifetime_default(span), 1)
} }
fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>)
-> Result<ty::Region, Option<Vec<ElisionFailureInfo>>>
{
self.base_scope.anon_region(span, def).map(|r| ty::fold::shift_region(r, 1))
}
fn anon_type_scope(&self) -> Option<AnonTypeScope> { fn anon_type_scope(&self) -> Option<AnonTypeScope> {
self.base_scope.anon_type_scope() self.base_scope.anon_type_scope()
} }

View file

@ -23,5 +23,17 @@ type MyStr = &str;
//~^ ERROR E0106 //~^ ERROR E0106
//~| NOTE expected lifetime parameter //~| NOTE expected lifetime parameter
struct Baz<'a>(&'a str);
struct Buzz<'a, 'b>(&'a str, &'b str);
struct Quux {
baz: Baz,
//~^ ERROR E0106
//~| expected lifetime parameter
buzz: Buzz,
//~^ ERROR E0106
//~| expected 2 lifetime parameters
}
fn main() { fn main() {
} }

View file

@ -18,9 +18,6 @@ enum Bar {
} }
struct Baz<'a, 'b, 'c> { struct Baz<'a, 'b, 'c> {
foo: Foo,
//~^ ERROR E0107
//~| expected 1 lifetime parameter
buzz: Buzz<'a>, buzz: Buzz<'a>,
//~^ ERROR E0107 //~^ ERROR E0107
//~| expected 2 lifetime parameters //~| expected 2 lifetime parameters

View file

@ -22,10 +22,11 @@ struct SomeStruct<I : for<'x> Foo<&'x isize>> {
//~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
} }
struct AnotherStruct<I : for<'x> Foo<&'x isize>> { // FIXME(eddyb) This one doesn't even compile because of the unsupported syntax.
field: <I as Foo<&isize>>::A
//~^ ERROR missing lifetime specifier // struct AnotherStruct<I : for<'x> Foo<&'x isize>> {
} // field: <I as for<'y> Foo<&'y isize>>::A
// }
struct YetAnotherStruct<'a, I : for<'x> Foo<&'x isize>> { struct YetAnotherStruct<'a, I : for<'x> Foo<&'x isize>> {
field: <I as Foo<&'a isize>>::A field: <I as Foo<&'a isize>>::A

View file

@ -38,4 +38,28 @@ fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier
panic!() panic!()
} }
// Cases which used to work but now don't.
type StaticStr = &'static str; // hides 'static
trait WithLifetime<'a> {
type Output; // can hide 'a
}
// This worked because the type of the first argument contains
// 'static, although StaticStr doesn't even have parameters.
fn j(_x: StaticStr) -> &isize { //~ ERROR missing lifetime specifier
//~^ HELP this function's return type contains a borrowed value
//~| HELP consider giving it an explicit bounded or 'static lifetime
panic!()
}
// This worked because the compiler resolved the argument type
// to <T as WithLifetime<'a>>::Output which has the hidden 'a.
fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize {
//~^ ERROR missing lifetime specifier
//~| HELP this function's return type contains a borrowed value
//~| HELP consider giving it an explicit bounded or 'static lifetime
panic!()
}
fn main() {} fn main() {}

View file

@ -15,7 +15,9 @@ fn non_elidable<'a, 'b>(a: &'a u8, b: &'b u8) -> &'a u8 {
} }
// the boundaries of elision // the boundaries of elision
static NON_ELIDABLE_FN: &fn(&u8, &u8) -> &u8 = &(non_elidable as fn(&u8, &u8) -> &u8); static NON_ELIDABLE_FN: &fn(&u8, &u8) -> &u8 =
//~^ ERROR missing lifetime specifier [E0106]
&(non_elidable as fn(&u8, &u8) -> &u8);
//~^ ERROR missing lifetime specifier [E0106] //~^ ERROR missing lifetime specifier [E0106]
struct SomeStruct<'x, 'y, 'z: 'x> { struct SomeStruct<'x, 'y, 'z: 'x> {