rustc: move most of lifetime elision to resolve_lifetimes.
This commit is contained in:
parent
bbc341424c
commit
ba1849daec
13 changed files with 703 additions and 602 deletions
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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={:?}", ®ions, 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() {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 ¶m.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(&()), ®ion_pred.lifetime);
|
||||
let r1 = AstConv::ast_region_to_region(&ccx.icx(&()), ®ion_pred.lifetime, None);
|
||||
for bound in ®ion_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>,
|
||||
|
|
|
@ -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##"
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue