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.
"##,
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##"
You tried to give a type parameter to a type which doesn't need it. Erroneous
code example:

View file

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

View file

@ -22,11 +22,16 @@ use hir::def::Def;
use hir::def_id::DefId;
use middle::region;
use ty;
use std::cell::Cell;
use std::mem::replace;
use syntax::ast;
use syntax::ptr::P;
use syntax::symbol::keywords;
use syntax_pos::Span;
use errors::DiagnosticBuilder;
use util::nodemap::{NodeMap, FxHashSet, FxHashMap};
use rustc_back::slice;
use hir;
use hir::intravisit::{self, Visitor, NestedVisitorMap};
@ -36,6 +41,7 @@ pub enum Region {
Static,
EarlyBound(/* index */ u32, /* lifetime decl */ ast::NodeId),
LateBound(ty::DebruijnIndex, /* lifetime decl */ ast::NodeId),
LateBoundAnon(ty::DebruijnIndex, /* anon index */ u32),
Free(region::CallSiteScopeData, /* lifetime decl */ ast::NodeId),
}
@ -51,9 +57,18 @@ impl Region {
(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> {
match *self {
Region::Static => None,
Region::Static |
Region::LateBoundAnon(..) => None,
Region::EarlyBound(_, id) |
Region::LateBound(_, id) |
Region::Free(_, id) => Some(id)
@ -65,6 +80,25 @@ impl Region {
Region::LateBound(depth, 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
}
}
@ -122,14 +156,46 @@ enum Scope<'a> {
/// 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.
/// 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 {
id: hir::BodyId,
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
}
#[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>;
const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
@ -189,12 +255,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
hir::ItemUse(..) |
hir::ItemMod(..) |
hir::ItemDefaultImpl(..) |
hir::ItemForeignMod(..) |
hir::ItemStatic(..) |
hir::ItemConst(..) => {
hir::ItemForeignMod(..) => {
// These sorts of items have no lifetime parameters at all.
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::ItemEnum(_, 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) {
if lifetime_ref.is_elided() {
self.resolve_elided_lifetimes(slice::ref_slice(lifetime_ref));
return;
}
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);
}
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) {
for ty_param in generics.ty_params.iter() {
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)
}
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)> {
@ -499,7 +594,9 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) {
label_span: Span) {
loop {
match *scope {
Scope::Body { s, .. } => { scope = s; }
Scope::Body { s, .. } |
Scope::Elision { s, .. } => { scope = s; }
Scope::Root => { return; }
Scope::Binder { ref lifetimes, s } => {
@ -639,6 +736,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
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]) {
for i in 0..lifetimes.len() {
let lifetime_i = &lifetimes[i];
@ -729,7 +1210,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
loop {
match *old_scope {
Scope::Body { s, .. } => {
Scope::Body { s, .. } |
Scope::Elision { 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_back::slice;
use require_c_abi_if_variadic;
use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope,
ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope,
ElisionFailureInfo, ElidedLifetime};
use rscope::{RegionScope, ObjectLifetimeDefaultRscope, ShiftedRscope};
use rscope::{AnonTypeScope, MaybeWithAnonTypes, ExplicitRscope};
use util::common::{ErrorReported, FN_OUTPUT_NAME};
use util::nodemap::{NodeMap, FxHashSet};
@ -74,7 +72,6 @@ use syntax::{abi, ast};
use syntax::feature_gate::{GateIssue, emit_feature_err};
use syntax::symbol::{Symbol, keywords};
use syntax_pos::Span;
use errors::DiagnosticBuilder;
pub trait AstConv<'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.
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?
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.
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 {
pub fn ast_region_to_region(&self, lifetime: &hir::Lifetime) -> &'tcx ty::Region {
self.opt_ast_region_to_region(&ExplicitRscope, lifetime.span, Some(lifetime), None)
}
fn try_opt_ast_region_to_region(&self,
rscope: &RegionScope,
default_span: Span,
opt_lifetime: Option<&hir::Lifetime>,
pub fn ast_region_to_region(&self,
lifetime: &hir::Lifetime,
def: Option<&ty::RegionParameterDef>)
-> Result<&'tcx ty::Region, Option<Vec<ElisionFailureInfo>>>
-> &'tcx ty::Region
{
let tcx = self.tcx();
let name = opt_lifetime.map(|l| l.name);
let resolved = opt_lifetime.and_then(|l| tcx.named_region_map.defs.get(&l.id));
let r = tcx.mk_region(match resolved {
let r = match tcx.named_region_map.defs.get(&lifetime.id) {
Some(&rl::Region::Static) => {
ty::ReStatic
tcx.mk_region(ty::ReStatic)
}
Some(&rl::Region::LateBound(debruijn, id)) => {
@ -263,16 +186,21 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
.get(&id)
.cloned()
.unwrap_or(ty::Issue32330::WontChange);
ty::ReLateBound(debruijn, ty::BrNamed(tcx.hir.local_def_id(id),
name.unwrap(),
issue_32330))
let name = tcx.hir.name(id);
tcx.mk_region(ty::ReLateBound(debruijn,
ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330)))
}
Some(&rl::Region::EarlyBound(index, _)) => {
ty::ReEarlyBound(ty::EarlyBoundRegion {
Some(&rl::Region::LateBoundAnon(debruijn, index)) => {
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,
name: name.unwrap()
})
name: name
}))
}
Some(&rl::Region::Free(scope, id)) => {
@ -283,47 +211,23 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
.get(&id)
.cloned()
.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),
bound_region: ty::BrNamed(tcx.hir.local_def_id(id),
name.unwrap(),
issue_32330)
})
bound_region: ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330)
}))
// (*) -- 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 {:?}",
opt_lifetime,
debug!("ast_region_to_region(lifetime={:?}) yields {:?}",
lifetime,
r);
Ok(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)
})
r
}
/// 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 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 {
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.
@ -445,11 +339,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
let mut output_assoc_binding = None;
let substs = Substs::for_item(tcx, def_id, |def, _| {
let i = def.index as usize - self_ty.is_some() as usize;
let l = lifetimes.get(i);
self.try_opt_ast_region_to_region(rscope, span, l, Some(def)).unwrap_or_else(|_| {
report_lifetime_count_mismatch();
if let Some(lifetime) = lifetimes.get(i) {
self.ast_region_to_region(lifetime, Some(def))
} else {
tcx.mk_region(ty::ReStatic)
})
}
}, |def, substs| {
let i = def.index as usize;
@ -533,72 +427,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
(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,
rscope: &RegionScope,
region_substs: &[Kind<'tcx>],
@ -606,19 +434,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
-> (Ty<'tcx>, ConvertedBinding<'tcx>)
{
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| {
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 {
Some(ref output_ty) => {
(self.convert_ty_with_lifetime_elision(implied_output_region,
&output_ty,
anon_scope),
output_ty.span)
(self.ast_ty_to_ty(&rscope, output_ty), output_ty.span)
}
None => {
(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) => {
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);
let rscope1 =
&ObjectLifetimeDefaultRscope::new(
@ -1489,9 +1312,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
let anon_scope = rscope.anon_type_scope();
let bare_fn_ty = self.ty_of_method_or_bare_fn(bf.unsafety,
bf.abi,
None,
&bf.decl,
None,
anon_scope,
anon_scope);
@ -1626,37 +1447,19 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
}
}
pub fn ty_of_method(&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,
pub fn ty_of_fn(&self,
unsafety: hir::Unsafety,
abi: abi::Abi,
decl: &hir::FnDecl,
body: hir::BodyId,
anon_scope: Option<AnonTypeScope>)
-> &'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,
unsafety: hir::Unsafety,
abi: abi::Abi,
opt_self_value_ty: Option<Ty<'tcx>>,
decl: &hir::FnDecl,
body: Option<hir::BodyId>,
arg_anon_scope: Option<AnonTypeScope>,
ret_anon_scope: Option<AnonTypeScope>)
-> &'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
// 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> =
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 {
hir::Return(ref output) =>
self.convert_ty_with_lifetime_elision(implied_output_region,
&output,
ret_anon_scope),
self.ast_ty_to_ty(&MaybeWithAnonTypes::new(ExplicitRscope, ret_anon_scope), output),
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={:?})",
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 expected_arg_ty = expected_sig.as_ref().and_then(|e| {
// no guarantee that the correct number of expected args
@ -1739,7 +1512,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
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());
@ -1755,7 +1528,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
expected_ret_ty.unwrap(),
_ if is_infer => self.ty_infer(decl.output.span()),
hir::Return(ref output) =>
self.ast_ty_to_ty(&rb, &output),
self.ast_ty_to_ty(&ExplicitRscope, &output),
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) {
// 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() {

View file

@ -97,7 +97,7 @@ use rustc::ty::adjustment;
use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
use rustc::ty::util::{Representability, IntTypeExt};
use require_c_abi_if_variadic;
use rscope::{ElisionFailureInfo, RegionScope};
use rscope::RegionScope;
use session::{Session, CompileResult};
use CrateCtxt;
use TypeAndSubsts;
@ -1410,6 +1410,15 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> {
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> {
self.next_ty_var(TypeVariableOrigin::TypeInference(span))
}
@ -1465,15 +1474,6 @@ impl<'a, 'gcx, 'tcx> RegionScope for FnCtxt<'a, 'gcx, 'tcx> {
// be some expression).
*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
@ -4408,7 +4408,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
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| {
let mut i = def.index as usize;

View file

@ -437,6 +437,11 @@ impl<'a, 'tcx> AstConv<'tcx, 'tcx> for ItemCtxt<'a, 'tcx> {
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> {
struct_span_err!(
self.tcx().sess,
@ -639,8 +644,6 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
container: AssociatedItemContainer,
id: ast::NodeId,
sig: &hir::MethodSig,
untransformed_rcvr_ty: Ty<'tcx>,
body: Option<hir::BodyId>,
rcvr_ty_predicates: &ty::GenericPredicates<'tcx>,) {
let def_id = ccx.tcx.hir.local_def_id(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)),
TraitContainer(_) => None
};
let assoc_item = ccx.tcx.associated_item(def_id);
let self_value_ty = if assoc_item.method_has_self_argument {
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 fty = AstConv::ty_of_fn(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)),
sig.unsafety, sig.abi, &sig.decl, anon_scope);
let substs = mk_item_substs(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)),
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);
}
hir::TraitItemKind::Method(ref sig, ref method) => {
let body = match *method {
hir::TraitMethod::Required(_) => None,
hir::TraitMethod::Provided(body) => Some(body)
};
hir::TraitItemKind::Method(ref sig, _) => {
convert_method(ccx, TraitContainer(trait_def_id),
trait_item.id, sig, tcx.mk_self_type(),
body, &trait_predicates);
trait_item.id, sig, &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_predicates = tcx.item_predicates(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 {
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));
}
hir::ImplItemKind::Method(ref sig, body) => {
convert_method(ccx, ImplContainer(impl_def_id),
impl_item.id, sig, impl_self_ty,
Some(body), &impl_predicates);
hir::ImplItemKind::Method(ref sig, _) => {
convert_method(ccx, ImplContainer(impl_def_id), impl_item.id, sig, &impl_predicates);
}
}
}
@ -1472,7 +1461,7 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
index: own_start + i as u32,
def_id: tcx.hir.local_def_id(l.lifetime.id),
bounds: l.bounds.iter().map(|l| {
AstConv::ast_region_to_region(&ccx.icx(&()), l)
AstConv::ast_region_to_region(&ccx.icx(&()), l, None)
}).collect(),
pure_wrt_drop: l.pure_wrt_drop,
}
@ -1545,11 +1534,11 @@ fn type_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
NodeItem(item) => {
match item.node {
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) => {
let tofd = AstConv::ty_of_bare_fn(&ccx.icx(generics), unsafety, abi, &decl,
body, Some(AnonTypeScope::new(def_id)));
ItemFn(ref decl, unsafety, _, abi, ref generics, _) => {
let tofd = AstConv::ty_of_fn(&ccx.icx(generics), unsafety, abi, &decl,
Some(AnonTypeScope::new(def_id)));
let substs = mk_item_substs(&ccx.icx(generics), item.span, def_id);
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
}));
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));
predicates.push(outlives.to_predicate());
}
@ -1816,7 +1805,9 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
}
&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));
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) => {
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 {
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));
predicates.push(ty::Predicate::RegionOutlives(pred))
}
@ -1935,7 +1926,7 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
hir::TraitTyParamBound(..) =>
None,
hir::RegionTyParamBound(ref lifetime) =>
Some(AstConv::ast_region_to_region(&ccx.icx(&()), lifetime)),
Some(AstConv::ast_region_to_region(&ccx.icx(&()), lifetime, None)),
}
})
.collect()
@ -1997,7 +1988,7 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>,
}).collect();
let region_bounds = region_bounds.into_iter().map(|r| {
astconv.ast_region_to_region(r)
astconv.ast_region_to_region(r, None)
}).collect();
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()
}
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));
vec![ty::Predicate::TypeOutlives(pred)]
}
@ -2057,18 +2048,7 @@ fn compute_type_of_foreign_fn_decl<'a, 'tcx>(
abi: abi::Abi)
-> Ty<'tcx>
{
let rb = BindingRscope::new();
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(),
};
let fty = AstConv::ty_of_fn(&ccx.icx(ast_generics), hir::Unsafety::Unsafe, abi, decl, None);
// feature gate SIMD types in FFI, since I (huonw) am not sure the
// ABIs are handled at all correctly.
@ -2084,21 +2064,17 @@ fn compute_type_of_foreign_fn_decl<'a, 'tcx>(
.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)
}
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 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 {
abi: abi,
unsafety: hir::Unsafety::Unsafe,
sig: ty::Binder(ccx.tcx.mk_fn_sig(input_tys.into_iter(), output, decl.variadic)),
}))
ccx.tcx.mk_fn_def(def_id, substs, fty)
}
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##"
This error means that an incorrect number of lifetime parameters were provided
for a type (like a struct or enum) or trait.
Some basic examples include:
for a type (like a struct or enum) or trait:
```compile_fail,E0107
struct Foo<'a>(&'a str);
struct Foo<'a, 'b>(&'a str, &'b str);
enum Bar { A, B, C }
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
}
```
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##"

View file

@ -8,28 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::ty;
use rustc::ty::subst::Substs;
use astconv::AstConv;
use std::cell::Cell;
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
/// example, if one writes the type `&Foo`, then the lifetime of
/// 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
/// regions can legally be omitted.
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
/// be derived from the object traits, what should we use? If
/// `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)
}
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 {
self.base_scope.base_object_lifetime_default(span)
}
@ -135,11 +113,6 @@ impl<R: RegionScope> RegionScope for MaybeWithAnonTypes<R> {
pub struct 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> {
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.
pub struct ObjectLifetimeDefaultRscope<'r> {
base_scope: &'r (RegionScope+'r),
@ -315,12 +159,6 @@ impl<'r> RegionScope for ObjectLifetimeDefaultRscope<'r> {
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> {
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)
}
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> {
self.base_scope.anon_type_scope()
}

View file

@ -23,5 +23,17 @@ type MyStr = &str;
//~^ ERROR E0106
//~| 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() {
}

View file

@ -18,9 +18,6 @@ enum Bar {
}
struct Baz<'a, 'b, 'c> {
foo: Foo,
//~^ ERROR E0107
//~| expected 1 lifetime parameter
buzz: Buzz<'a>,
//~^ ERROR E0107
//~| 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
}
struct AnotherStruct<I : for<'x> Foo<&'x isize>> {
field: <I as Foo<&isize>>::A
//~^ ERROR missing lifetime specifier
}
// FIXME(eddyb) This one doesn't even compile because of the unsupported syntax.
// 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>> {
field: <I as Foo<&'a isize>>::A

View file

@ -38,4 +38,28 @@ fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier
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() {}

View file

@ -15,7 +15,9 @@ fn non_elidable<'a, 'b>(a: &'a u8, b: &'b u8) -> &'a u8 {
}
// 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]
struct SomeStruct<'x, 'y, 'z: 'x> {