about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-01-13 15:09:56 +0200
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2017-01-28 02:56:46 +0200
commitba1849daecf0ae8fee54cc32f378809a9531e5ed (patch)
tree52a212d16ae5ae8a28a47102582676f81a7a60bf
parentbbc341424cd19f1d0a66fb2df78b22a3ad0e6856 (diff)
downloadrust-ba1849daecf0ae8fee54cc32f378809a9531e5ed.tar.gz
rust-ba1849daecf0ae8fee54cc32f378809a9531e5ed.zip
rustc: move most of lifetime elision to resolve_lifetimes.
-rw-r--r--src/librustc/diagnostics.rs63
-rw-r--r--src/librustc/hir/lowering.rs3
-rw-r--r--src/librustc/middle/resolve_lifetime.rs502
-rw-r--r--src/librustc_typeck/astconv.rs337
-rw-r--r--src/librustc_typeck/check/mod.rs26
-rw-r--r--src/librustc_typeck/collect.rs82
-rw-r--r--src/librustc_typeck/diagnostics.rs72
-rw-r--r--src/librustc_typeck/rscope.rs168
-rw-r--r--src/test/compile-fail/E0106.rs12
-rw-r--r--src/test/compile-fail/E0107.rs3
-rw-r--r--src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs9
-rw-r--r--src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs24
-rw-r--r--src/test/compile-fail/rfc1623.rs4
13 files changed, 703 insertions, 602 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 2878ff5e284..b51a7d4104a 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -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:
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 4160ec5b74a..7ca251f3ff9 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -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))
                 }
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index c76ce9dac6f..4e02485a40b 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -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;
                 }
 
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 3338daeb774..42a220e1b9b 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -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::LateBoundAnon(debruijn, index)) => {
+                tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index)))
             }
 
-            Some(&rl::Region::EarlyBound(index, _)) => {
-                ty::ReEarlyBound(ty::EarlyBoundRegion {
+            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 {
-                        scope: scope.to_code_extent(&tcx.region_maps),
-                        bound_region: ty::BrNamed(tcx.hir.local_def_id(id),
-                                                  name.unwrap(),
-                                                  issue_32330)
-                })
+                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, 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,
-                         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)
+    pub fn ty_of_fn(&self,
+                    unsafety: hir::Unsafety,
+                    abi: abi::Abi,
+                    decl: &hir::FnDecl,
+                    anon_scope: Option<AnonTypeScope>)
+                    -> &'tcx ty::BareFnTy<'tcx> {
+        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() {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index e7544c10be3..a8b5d718f81 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -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;
 
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index f954d2a5d61..70bd43751eb 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -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>,
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 6d943f3ca2e..7f8c508bf22 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -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##"
diff --git a/src/librustc_typeck/rscope.rs b/src/librustc_typeck/rscope.rs
index 2ad1a7c3d68..3ac917c396c 100644
--- a/src/librustc_typeck/rscope.rs
+++ b/src/librustc_typeck/rscope.rs
@@ -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,105 +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))
-    }
-
-    fn base_object_lifetime_default(&self, _span: Span) -> ty::Region {
-        ty::ReStatic
-    }
-}
-
-// 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))
     }
@@ -243,41 +122,6 @@ impl<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> RegionScope for StaticRscope<'a, 'gcx, 'tcx>
     }
 }
 
-/// 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()
     }
diff --git a/src/test/compile-fail/E0106.rs b/src/test/compile-fail/E0106.rs
index dab03f0bccf..d5644ab0608 100644
--- a/src/test/compile-fail/E0106.rs
+++ b/src/test/compile-fail/E0106.rs
@@ -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() {
 }
diff --git a/src/test/compile-fail/E0107.rs b/src/test/compile-fail/E0107.rs
index 5f333e17c47..16ebd3e9ca5 100644
--- a/src/test/compile-fail/E0107.rs
+++ b/src/test/compile-fail/E0107.rs
@@ -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
diff --git a/src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs b/src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs
index 44ad0bb0113..e6251a0d318 100644
--- a/src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs
+++ b/src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs
@@ -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
diff --git a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs
index 7355c70ff95..43371eb6340 100644
--- a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs
+++ b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs
@@ -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() {}
diff --git a/src/test/compile-fail/rfc1623.rs b/src/test/compile-fail/rfc1623.rs
index 083cc218eec..93635e7fdde 100644
--- a/src/test/compile-fail/rfc1623.rs
+++ b/src/test/compile-fail/rfc1623.rs
@@ -15,8 +15,10 @@ 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> {
     foo: &'x Foo<'z>,