]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_typeck/check/mod.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc_typeck / check / mod.rs
index 1d184131dede3c017b35d279018f1a922db35200..8fb0858bfec3a4d9056e6e36d154026c6a0aec4e 100644 (file)
@@ -1,21 +1,12 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+// ignore-tidy-filelength
 
-/*
+/*!
 
-# check.rs
+# typeck: check phase
 
 Within the check phase of type check, we check each item one at a time
 (bodies of function expressions are checked as part of the containing
-function).  Inference is used to supply types wherever they are
-unknown.
+function). Inference is used to supply types wherever they are unknown.
 
 By far the most complex case is checking the body of a function. This
 can be broken down into several distinct phases:
@@ -52,11 +43,11 @@ can be broken down into several distinct phases:
 
 While type checking a function, the intermediate types for the
 expressions, blocks, and so forth contained within the function are
-stored in `fcx.node_types` and `fcx.item_substs`.  These types
+stored in `fcx.node_types` and `fcx.node_substs`.  These types
 may contain unresolved type variables.  After type checking is
 complete, the functions in the writeback module are used to take the
 types from this table, resolve them, and then write them into their
-permanent home in the type context `ccx.tcx`.
+permanent home in the type context `tcx`.
 
 This means that during inferencing you should use `fcx.write_ty()`
 and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
@@ -65,7 +56,7 @@ nodes within the function.
 The types of top-level items, which never contain unbound type
 variables, are stored directly into the `tcx` tables.
 
-n.b.: A type variable is not the same thing as a type parameter.  A
+N.B., a type variable is not the same thing as a type parameter.  A
 type variable is rather an "instance" of a type parameter: that is,
 given a generic function `fn foo<T>(t: T)`: while checking the
 function `foo`, the type `ty_param(0)` refers to the type `T`, which
@@ -76,67 +67,131 @@ type parameter).
 
 */
 
-pub use self::LvaluePreference::*;
-pub use self::Expectation::*;
-use self::IsBinopAssignment::*;
-use self::TupleArgumentsFlag::*;
-
-use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv};
-use check::_match::pat_ctxt;
-use middle::{const_eval, def};
-use middle::infer;
-use middle::lang_items::IteratorItem;
-use middle::mem_categorization as mc;
-use middle::mem_categorization::McResult;
-use middle::pat_util::{self, pat_id_map};
-use middle::region::CodeExtent;
-use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace};
-use middle::traits;
-use middle::ty::{FnSig, VariantInfo, TypeScheme};
-use middle::ty::{Disr, ParamTy, ParameterEnvironment};
-use middle::ty::{self, HasProjectionTypes, RegionEscape, Ty};
-use middle::ty::liberate_late_bound_regions;
-use middle::ty::{MethodCall, MethodCallee, MethodMap, ObjectCastMap};
-use middle::ty_fold::{TypeFolder, TypeFoldable};
-use rscope::RegionScope;
-use session::Session;
-use {CrateCtxt, lookup_def_ccx, no_params, require_same_types};
-use TypeAndSubsts;
-use middle::lang_items::TypeIdLangItem;
-use lint;
-use util::common::{block_query, indenter, loop_query};
-use util::ppaux::{self, UserString, Repr};
-use util::nodemap::{DefIdMap, FnvHashMap, NodeMap};
-
-use std::cell::{Cell, Ref, RefCell};
-use std::mem::replace;
-use std::rc::Rc;
-use std::iter::repeat;
-use syntax::{self, abi, attr};
-use syntax::ast::{self, ProvidedMethod, RequiredMethod, TypeTraitItem, DefId};
-use syntax::ast_util::{self, local_def, PostExpansionMethod};
-use syntax::codemap::{self, Span};
-use syntax::owned_slice::OwnedSlice;
-use syntax::parse::token;
-use syntax::print::pprust;
-use syntax::ptr::P;
-use syntax::visit::{self, Visitor};
-
-mod assoc;
+mod autoderef;
+pub mod dropck;
 pub mod _match;
-pub mod vtable;
+mod pat;
 pub mod writeback;
-pub mod regionmanip;
-pub mod regionck;
+mod regionck;
+pub mod coercion;
 pub mod demand;
+mod expr;
 pub mod method;
 mod upvar;
-pub mod wf;
+mod wfcheck;
+mod cast;
 mod closure;
 mod callee;
+mod compare_method;
+mod generator_interior;
+pub mod intrinsic;
+mod op;
+
+use crate::astconv::{AstConv, PathSeg};
+use errors::{Applicability, DiagnosticBuilder, DiagnosticId, pluralize};
+use rustc::hir::{self, ExprKind, GenericArg, ItemKind, Node, PatKind, QPath};
+use rustc::hir::def::{CtorOf, Res, DefKind};
+use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
+use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
+use rustc::hir::itemlikevisit::ItemLikeVisitor;
+use rustc::hir::ptr::P;
+use crate::middle::lang_items;
+use crate::namespace::Namespace;
+use rustc::infer::{self, InferCtxt, InferOk, InferResult};
+use rustc::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
+use rustc_index::vec::Idx;
+use rustc_target::spec::abi::Abi;
+use rustc::infer::opaque_types::OpaqueTypeDecl;
+use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc::infer::error_reporting::TypeAnnotationNeeded::E0282;
+use rustc::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc::middle::region;
+use rustc::mir::interpret::{ConstValue, GlobalId};
+use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
+use rustc::ty::{
+    self, AdtKind, CanonicalUserType, Ty, TyCtxt, Const, GenericParamDefKind,
+    ToPolyTraitRef, ToPredicate, RegionKind, UserType
+};
+use rustc::ty::adjustment::{
+    Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast
+};
+use rustc::ty::fold::{TypeFoldable, TypeFolder};
+use rustc::ty::query::Providers;
+use rustc::ty::subst::{
+    GenericArgKind, Subst, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts,
+};
+use rustc::ty::util::{Representability, IntTypeExt, Discr};
+use rustc::ty::layout::VariantIdx;
+use syntax_pos::{self, BytePos, Span, MultiSpan};
+use syntax_pos::hygiene::DesugaringKind;
+use syntax::ast;
+use syntax::attr;
+use syntax::feature_gate::feature_err;
+use syntax::source_map::{DUMMY_SP, original_sp};
+use syntax::symbol::{kw, sym, Ident};
+use syntax::util::parser::ExprPrecedence;
+
+use rustc_error_codes::*;
+
+use std::cell::{Cell, RefCell, Ref, RefMut};
+use std::collections::hash_map::Entry;
+use std::cmp;
+use std::iter;
+use std::mem::replace;
+use std::ops::{self, Deref};
+use std::slice;
+
+use crate::require_c_abi_if_c_variadic;
+use crate::session::Session;
+use crate::session::config::EntryFnType;
+use crate::TypeAndSubsts;
+use crate::lint;
+use crate::util::captures::Captures;
+use crate::util::common::{ErrorReported, indenter};
+use crate::util::nodemap::{DefIdMap, DefIdSet, FxHashMap, FxHashSet, HirIdMap};
+
+pub use self::Expectation::*;
+use self::autoderef::Autoderef;
+use self::callee::DeferredCallResolution;
+use self::coercion::{CoerceMany, DynamicCoerceMany};
+pub use self::compare_method::{compare_impl_method, compare_const_impl};
+use self::method::{MethodCallee, SelfSource};
+use self::TupleArgumentsFlag::*;
+
+/// The type of a local binding, including the revealed type for anon types.
+#[derive(Copy, Clone, Debug)]
+pub struct LocalTy<'tcx> {
+    decl_ty: Ty<'tcx>,
+    revealed_ty: Ty<'tcx>
+}
+
+/// A wrapper for `InferCtxt`'s `in_progress_tables` field.
+#[derive(Copy, Clone)]
+struct MaybeInProgressTables<'a, 'tcx> {
+    maybe_tables: Option<&'a RefCell<ty::TypeckTables<'tcx>>>,
+}
+
+impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> {
+    fn borrow(self) -> Ref<'a, ty::TypeckTables<'tcx>> {
+        match self.maybe_tables {
+            Some(tables) => tables.borrow(),
+            None => {
+                bug!("MaybeInProgressTables: inh/fcx.tables.borrow() with no tables")
+            }
+        }
+    }
+
+    fn borrow_mut(self) -> RefMut<'a, ty::TypeckTables<'tcx>> {
+        match self.maybe_tables {
+            Some(tables) => tables.borrow_mut(),
+            None => {
+                bug!("MaybeInProgressTables: inh/fcx.tables.borrow_mut() with no tables")
+            }
+        }
+    }
+}
 
-/// Fields that are part of a `FnCtxt` which are inherited by
-/// closures defined within the function.  For example:
+/// Closures defined within the function. For example:
 ///
 ///     fn foo() {
 ///         bar(move|| { ... })
@@ -145,40 +200,74 @@ mod callee;
 /// Here, the function `foo()` and the closure passed to
 /// `bar()` will each have their own `FnCtxt`, but they will
 /// share the inherited fields.
-pub struct Inherited<'a, 'tcx: 'a> {
-    infcx: infer::InferCtxt<'a, 'tcx>,
-    locals: RefCell<NodeMap<Ty<'tcx>>>,
-    param_env: ty::ParameterEnvironment<'a, 'tcx>,
-
-    // Temporary tables:
-    node_types: RefCell<NodeMap<Ty<'tcx>>>,
-    item_substs: RefCell<NodeMap<ty::ItemSubsts<'tcx>>>,
-    adjustments: RefCell<NodeMap<ty::AutoAdjustment<'tcx>>>,
-    method_map: MethodMap<'tcx>,
-    upvar_borrow_map: RefCell<ty::UpvarBorrowMap>,
-    unboxed_closures: RefCell<DefIdMap<ty::UnboxedClosure<'tcx>>>,
-    object_cast_map: ObjectCastMap<'tcx>,
-
-    // A mapping from each fn's id to its signature, with all bound
-    // regions replaced with free ones. Unlike the other tables, this
-    // one is never copied into the tcx: it is only used by regionck.
-    fn_sig_map: RefCell<NodeMap<Vec<Ty<'tcx>>>>,
-
-    // Tracks trait obligations incurred during this function body.
-    fulfillment_cx: RefCell<traits::FulfillmentContext<'tcx>>,
+pub struct Inherited<'a, 'tcx> {
+    infcx: InferCtxt<'a, 'tcx>,
+
+    tables: MaybeInProgressTables<'a, 'tcx>,
+
+    locals: RefCell<HirIdMap<LocalTy<'tcx>>>,
+
+    fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>,
+
+    // Some additional `Sized` obligations badly affect type inference.
+    // These obligations are added in a later stage of typeck.
+    deferred_sized_obligations: RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>,
+
+    // When we process a call like `c()` where `c` is a closure type,
+    // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or
+    // `FnOnce` closure. In that case, we defer full resolution of the
+    // call until upvar inference can kick in and make the
+    // decision. We keep these deferred resolutions grouped by the
+    // def-id of the closure, so that once we decide, we can easily go
+    // back and process them.
+    deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolution<'tcx>>>>,
+
+    deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>,
+
+    deferred_generator_interiors: RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
+
+    // Opaque types found in explicit return types and their
+    // associated fresh inference variable. Writeback resolves these
+    // variables to get the concrete type, which can be used to
+    // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
+    opaque_types: RefCell<DefIdMap<OpaqueTypeDecl<'tcx>>>,
+
+    /// A map from inference variables created from opaque
+    /// type instantiations (`ty::Infer`) to the actual opaque
+    /// type (`ty::Opaque`). Used during fallback to map unconstrained
+    /// opaque type inference variables to their corresponding
+    /// opaque type.
+    opaque_types_vars: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,
+
+    /// Each type parameter has an implicit region bound that
+    /// indicates it must outlive at least the function body (the user
+    /// may specify stronger requirements). This field indicates the
+    /// region of the callee. If it is `None`, then the parameter
+    /// environment is for an item or something where the "callee" is
+    /// not clear.
+    implicit_region_bound: Option<ty::Region<'tcx>>,
+
+    body_id: Option<hir::BodyId>,
+}
+
+impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
+    type Target = InferCtxt<'a, 'tcx>;
+    fn deref(&self) -> &Self::Target {
+        &self.infcx
+    }
 }
 
 /// When type-checking an expression, we propagate downward
 /// whatever type hint we are able in the form of an `Expectation`.
-#[derive(Copy)]
-enum Expectation<'tcx> {
+#[derive(Copy, Clone, Debug)]
+pub enum Expectation<'tcx> {
     /// We know nothing about what type this expression should have.
     NoExpectation,
 
-    /// This expression should have the type given (or some subtype)
+    /// This expression should have the type given (or some subtype).
     ExpectHasType(Ty<'tcx>),
 
-    /// This expression will be cast to the `Ty`
+    /// This expression will be cast to the `Ty`.
     ExpectCastableToType(Ty<'tcx>),
 
     /// This rvalue expression will be wrapped in `&` or `Box` and coerced
@@ -186,7 +275,7 @@ enum Expectation<'tcx> {
     ExpectRvalueLikeUnsized(Ty<'tcx>),
 }
 
-impl<'tcx> Expectation<'tcx> {
+impl<'a, 'tcx> Expectation<'tcx> {
     // Disregard "castable to" expectations because they
     // can lead us astray. Consider for example `if cond
     // {22} else {c} as u8` -- if we propagate the
@@ -203,11 +292,11 @@ impl<'tcx> Expectation<'tcx> {
     // an expected type. Otherwise, we might write parts of the type
     // when checking the 'then' block which are incompatible with the
     // 'else' branch.
-    fn adjust_for_branches<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
+    fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
         match *self {
             ExpectHasType(ety) => {
-                let ety = fcx.infcx().shallow_resolve(ety);
-                if !ty::type_is_ty_var(ety) {
+                let ety = fcx.shallow_resolve(ety);
+                if !ety.is_ty_var() {
                     ExpectHasType(ety)
                 } else {
                     NoExpectation
@@ -219,5588 +308,5187 @@ impl<'tcx> Expectation<'tcx> {
             _ => NoExpectation
         }
     }
+
+    /// Provides an expectation for an rvalue expression given an *optional*
+    /// hint, which is not required for type safety (the resulting type might
+    /// be checked higher up, as is the case with `&expr` and `box expr`), but
+    /// is useful in determining the concrete type.
+    ///
+    /// The primary use case is where the expected type is a fat pointer,
+    /// like `&[isize]`. For example, consider the following statement:
+    ///
+    ///    let x: &[isize] = &[1, 2, 3];
+    ///
+    /// In this case, the expected type for the `&[1, 2, 3]` expression is
+    /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
+    /// expectation `ExpectHasType([isize])`, that would be too strong --
+    /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`.
+    /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
+    /// to the type `&[isize]`. Therefore, we propagate this more limited hint,
+    /// which still is useful, because it informs integer literals and the like.
+    /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
+    /// for examples of where this comes up,.
+    fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
+        match fcx.tcx.struct_tail_without_normalization(ty).kind {
+            ty::Slice(_) | ty::Str | ty::Dynamic(..) => {
+                ExpectRvalueLikeUnsized(ty)
+            }
+            _ => ExpectHasType(ty)
+        }
+    }
+
+    // Resolves `expected` by a single level if it is a variable. If
+    // there is no expected type or resolution is not possible (e.g.,
+    // no constraints yet present), just returns `None`.
+    fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
+        match self {
+            NoExpectation => NoExpectation,
+            ExpectCastableToType(t) => {
+                ExpectCastableToType(fcx.resolve_vars_if_possible(&t))
+            }
+            ExpectHasType(t) => {
+                ExpectHasType(fcx.resolve_vars_if_possible(&t))
+            }
+            ExpectRvalueLikeUnsized(t) => {
+                ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t))
+            }
+        }
+    }
+
+    fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
+        match self.resolve(fcx) {
+            NoExpectation => None,
+            ExpectCastableToType(ty) |
+            ExpectHasType(ty) |
+            ExpectRvalueLikeUnsized(ty) => Some(ty),
+        }
+    }
+
+    /// It sometimes happens that we want to turn an expectation into
+    /// a **hard constraint** (i.e., something that must be satisfied
+    /// for the program to type-check). `only_has_type` will return
+    /// such a constraint, if it exists.
+    fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
+        match self.resolve(fcx) {
+            ExpectHasType(ty) => Some(ty),
+            NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None,
+        }
+    }
+
+    /// Like `only_has_type`, but instead of returning `None` if no
+    /// hard constraint exists, creates a fresh type variable.
+    fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> {
+        self.only_has_type(fcx)
+            .unwrap_or_else(|| {
+                fcx.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::MiscVariable,
+                    span,
+                })
+            })
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Needs {
+    MutPlace,
+    None
+}
+
+impl Needs {
+    fn maybe_mut_place(m: hir::Mutability) -> Self {
+        match m {
+            hir::Mutability::Mutable => Needs::MutPlace,
+            hir::Mutability::Immutable => Needs::None,
+        }
+    }
 }
 
 #[derive(Copy, Clone)]
 pub struct UnsafetyState {
-    pub def: ast::NodeId,
-    pub unsafety: ast::Unsafety,
+    pub def: hir::HirId,
+    pub unsafety: hir::Unsafety,
+    pub unsafe_push_count: u32,
     from_fn: bool
 }
 
 impl UnsafetyState {
-    pub fn function(unsafety: ast::Unsafety, def: ast::NodeId) -> UnsafetyState {
-        UnsafetyState { def: def, unsafety: unsafety, from_fn: true }
+    pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState {
+        UnsafetyState { def, unsafety, unsafe_push_count: 0, from_fn: true }
     }
 
-    pub fn recurse(&mut self, blk: &ast::Block) -> UnsafetyState {
+    pub fn recurse(&mut self, blk: &hir::Block) -> UnsafetyState {
         match self.unsafety {
             // If this unsafe, then if the outer function was already marked as
             // unsafe we shouldn't attribute the unsafe'ness to the block. This
             // way the block can be warned about instead of ignoring this
             // extraneous block (functions are never warned about).
-            ast::Unsafety::Unsafe if self.from_fn => *self,
+            hir::Unsafety::Unsafe if self.from_fn => *self,
 
             unsafety => {
-                let (unsafety, def) = match blk.rules {
-                    ast::UnsafeBlock(..) => (ast::Unsafety::Unsafe, blk.id),
-                    ast::DefaultBlock => (unsafety, self.def),
+                let (unsafety, def, count) = match blk.rules {
+                    hir::PushUnsafeBlock(..) =>
+                        (unsafety, blk.hir_id, self.unsafe_push_count.checked_add(1).unwrap()),
+                    hir::PopUnsafeBlock(..) =>
+                        (unsafety, blk.hir_id, self.unsafe_push_count.checked_sub(1).unwrap()),
+                    hir::UnsafeBlock(..) =>
+                        (hir::Unsafety::Unsafe, blk.hir_id, self.unsafe_push_count),
+                    hir::DefaultBlock =>
+                        (unsafety, self.def, self.unsafe_push_count),
                 };
-                UnsafetyState{ def: def,
-                             unsafety: unsafety,
-                             from_fn: false }
+                UnsafetyState{ def,
+                               unsafety,
+                               unsafe_push_count: count,
+                               from_fn: false }
             }
         }
     }
 }
 
-/// Whether `check_binop` is part of an assignment or not.
-/// Used to know whether we allow user overloads and to print
-/// better messages on error.
-#[derive(PartialEq)]
-enum IsBinopAssignment{
-    SimpleBinop,
-    BinopAssignment,
+#[derive(Debug, Copy, Clone)]
+pub enum PlaceOp {
+    Deref,
+    Index
 }
 
-#[derive(Clone)]
-pub struct FnCtxt<'a, 'tcx: 'a> {
-    body_id: ast::NodeId,
-
-    // This flag is set to true if, during the writeback phase, we encounter
-    // a type error in this function.
-    writeback_errors: Cell<bool>,
-
-    // Number of errors that had been reported when we started
-    // checking this function. On exit, if we find that *more* errors
-    // have been reported, we will skip regionck and other work that
-    // expects the types within the function to be consistent.
-    err_count_on_creation: uint,
-
-    ret_ty: ty::FnOutput<'tcx>,
-
-    ps: RefCell<UnsafetyState>,
+/// Tracks whether executing a node may exit normally (versus
+/// return/break/panic, which "diverge", leaving dead code in their
+/// wake). Tracked semi-automatically (through type variables marked
+/// as diverging), with some manual adjustments for control-flow
+/// primitives (approximating a CFG).
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Diverges {
+    /// Potentially unknown, some cases converge,
+    /// others require a CFG to determine them.
+    Maybe,
+
+    /// Definitely known to diverge and therefore
+    /// not reach the next sibling or its parent.
+    Always {
+        /// The `Span` points to the expression
+        /// that caused us to diverge
+        /// (e.g. `return`, `break`, etc).
+        span: Span,
+        /// In some cases (e.g. a `match` expression
+        /// where all arms diverge), we may be
+        /// able to provide a more informative
+        /// message to the user.
+        /// If this is `None`, a default messsage
+        /// will be generated, which is suitable
+        /// for most cases.
+        custom_note: Option<&'static str>
+    },
+
+    /// Same as `Always` but with a reachability
+    /// warning already emitted.
+    WarnedAlways
+}
 
-    inh: &'a Inherited<'a, 'tcx>,
+// Convenience impls for combining `Diverges`.
 
-    ccx: &'a CrateCtxt<'a, 'tcx>,
+impl ops::BitAnd for Diverges {
+    type Output = Self;
+    fn bitand(self, other: Self) -> Self {
+        cmp::min(self, other)
+    }
 }
 
-impl<'a, 'tcx> mc::Typer<'tcx> for FnCtxt<'a, 'tcx> {
-    fn tcx(&self) -> &ty::ctxt<'tcx> {
-        self.ccx.tcx
-    }
-    fn node_ty(&self, id: ast::NodeId) -> McResult<Ty<'tcx>> {
-        let ty = self.node_ty(id);
-        self.resolve_type_vars_or_error(&ty)
+impl ops::BitOr for Diverges {
+    type Output = Self;
+    fn bitor(self, other: Self) -> Self {
+        cmp::max(self, other)
     }
-    fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult<Ty<'tcx>> {
-        let ty = self.adjust_expr_ty(expr, self.inh.adjustments.borrow().get(&expr.id));
-        self.resolve_type_vars_or_error(&ty)
-    }
-    fn type_moves_by_default(&self, span: Span, ty: Ty<'tcx>) -> bool {
-        let ty = self.infcx().resolve_type_vars_if_possible(&ty);
-        traits::type_known_to_meet_builtin_bound(self.infcx(), self, ty, ty::BoundCopy, span)
-    }
-    fn node_method_ty(&self, method_call: ty::MethodCall)
-                      -> Option<Ty<'tcx>> {
-        self.inh.method_map.borrow()
-                           .get(&method_call)
-                           .map(|method| method.ty)
-                           .map(|ty| self.infcx().resolve_type_vars_if_possible(&ty))
-    }
-    fn node_method_origin(&self, method_call: ty::MethodCall)
-                          -> Option<ty::MethodOrigin<'tcx>>
-    {
-        self.inh.method_map.borrow()
-                           .get(&method_call)
-                           .map(|method| method.origin.clone())
+}
+
+impl ops::BitAndAssign for Diverges {
+    fn bitand_assign(&mut self, other: Self) {
+        *self = *self & other;
     }
-    fn adjustments(&self) -> &RefCell<NodeMap<ty::AutoAdjustment<'tcx>>> {
-        &self.inh.adjustments
+}
+
+impl ops::BitOrAssign for Diverges {
+    fn bitor_assign(&mut self, other: Self) {
+        *self = *self | other;
     }
-    fn is_method_call(&self, id: ast::NodeId) -> bool {
-        self.inh.method_map.borrow().contains_key(&ty::MethodCall::expr(id))
+}
+
+impl Diverges {
+    /// Creates a `Diverges::Always` with the provided `span` and the default note message.
+    fn always(span: Span) -> Diverges {
+        Diverges::Always {
+            span,
+            custom_note: None
+        }
     }
-    fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<CodeExtent> {
-        self.param_env().temporary_scope(rvalue_id)
+
+    fn is_always(self) -> bool {
+        // Enum comparison ignores the
+        // contents of fields, so we just
+        // fill them in with garbage here.
+        self >= Diverges::Always {
+            span: DUMMY_SP,
+            custom_note: None
+        }
     }
-    fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarBorrow> {
-        self.inh.upvar_borrow_map.borrow().get(&upvar_id).cloned()
+}
+
+pub struct BreakableCtxt<'tcx> {
+    may_break: bool,
+
+    // this is `null` for loops where break with a value is illegal,
+    // such as `while`, `for`, and `while let`
+    coerce: Option<DynamicCoerceMany<'tcx>>,
+}
+
+pub struct EnclosingBreakables<'tcx> {
+    stack: Vec<BreakableCtxt<'tcx>>,
+    by_id: HirIdMap<usize>,
+}
+
+impl<'tcx> EnclosingBreakables<'tcx> {
+    fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> {
+        self.opt_find_breakable(target_id).unwrap_or_else(|| {
+            bug!("could not find enclosing breakable with id {}", target_id);
+        })
     }
-    fn capture_mode(&self, closure_expr_id: ast::NodeId)
-                    -> ast::CaptureClause {
-        self.ccx.tcx.capture_mode(closure_expr_id)
+
+    fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> {
+        match self.by_id.get(&target_id) {
+            Some(ix) => Some(&mut self.stack[*ix]),
+            None => None,
+        }
     }
 }
 
-impl<'a, 'tcx> ty::UnboxedClosureTyper<'tcx> for FnCtxt<'a, 'tcx> {
-    fn param_env<'b>(&'b self) -> &'b ty::ParameterEnvironment<'b,'tcx> {
-        &self.inh.param_env
-    }
+pub struct FnCtxt<'a, 'tcx> {
+    body_id: hir::HirId,
+
+    /// The parameter environment used for proving trait obligations
+    /// in this function. This can change when we descend into
+    /// closures (as they bring new things into scope), hence it is
+    /// not part of `Inherited` (as of the time of this writing,
+    /// closures do not yet change the environment, but they will
+    /// eventually).
+    param_env: ty::ParamEnv<'tcx>,
+
+    /// Number of errors that had been reported when we started
+    /// checking this function. On exit, if we find that *more* errors
+    /// have been reported, we will skip regionck and other work that
+    /// expects the types within the function to be consistent.
+    // FIXME(matthewjasper) This should not exist, and it's not correct
+    // if type checking is run in parallel.
+    err_count_on_creation: usize,
+
+    /// If `Some`, this stores coercion information for returned
+    /// expressions. If `None`, this is in a context where return is
+    /// inappropriate, such as a const expression.
+    ///
+    /// This is a `RefCell<DynamicCoerceMany>`, which means that we
+    /// can track all the return expressions and then use them to
+    /// compute a useful coercion from the set, similar to a match
+    /// expression or other branching context. You can use methods
+    /// like `expected_ty` to access the declared return type (if
+    /// any).
+    ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
 
-    fn unboxed_closure_kind(&self,
-                            def_id: ast::DefId)
-                            -> ty::UnboxedClosureKind
-    {
-        self.inh.unboxed_closures.borrow()[def_id].kind
+    /// First span of a return site that we find. Used in error messages.
+    ret_coercion_span: RefCell<Option<Span>>,
+
+    yield_ty: Option<Ty<'tcx>>,
+
+    ps: RefCell<UnsafetyState>,
+
+    /// Whether the last checked node generates a divergence (e.g.,
+    /// `return` will set this to `Always`). In general, when entering
+    /// an expression or other node in the tree, the initial value
+    /// indicates whether prior parts of the containing expression may
+    /// have diverged. It is then typically set to `Maybe` (and the
+    /// old value remembered) for processing the subparts of the
+    /// current expression. As each subpart is processed, they may set
+    /// the flag to `Always`, etc. Finally, at the end, we take the
+    /// result and "union" it with the original value, so that when we
+    /// return the flag indicates if any subpart of the parent
+    /// expression (up to and including this part) has diverged. So,
+    /// if you read it after evaluating a subexpression `X`, the value
+    /// you get indicates whether any subexpression that was
+    /// evaluating up to and including `X` diverged.
+    ///
+    /// We currently use this flag only for diagnostic purposes:
+    ///
+    /// - To warn about unreachable code: if, after processing a
+    ///   sub-expression but before we have applied the effects of the
+    ///   current node, we see that the flag is set to `Always`, we
+    ///   can issue a warning. This corresponds to something like
+    ///   `foo(return)`; we warn on the `foo()` expression. (We then
+    ///   update the flag to `WarnedAlways` to suppress duplicate
+    ///   reports.) Similarly, if we traverse to a fresh statement (or
+    ///   tail expression) from a `Always` setting, we will issue a
+    ///   warning. This corresponds to something like `{return;
+    ///   foo();}` or `{return; 22}`, where we would warn on the
+    ///   `foo()` or `22`.
+    ///
+    /// An expression represents dead code if, after checking it,
+    /// the diverges flag is set to something other than `Maybe`.
+    diverges: Cell<Diverges>,
+
+    /// Whether any child nodes have any type errors.
+    has_errors: Cell<bool>,
+
+    enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
+
+    inh: &'a Inherited<'a, 'tcx>,
+}
+
+impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
+    type Target = Inherited<'a, 'tcx>;
+    fn deref(&self) -> &Self::Target {
+        &self.inh
     }
+}
 
-    fn unboxed_closure_type(&self,
-                            def_id: ast::DefId,
-                            substs: &subst::Substs<'tcx>)
-                            -> ty::ClosureTy<'tcx>
-    {
-        self.inh.unboxed_closures.borrow()[def_id].closure_type.subst(self.tcx(), substs)
+/// Helper type of a temporary returned by `Inherited::build(...)`.
+/// Necessary because we can't write the following bound:
+/// `F: for<'b, 'tcx> where 'tcx FnOnce(Inherited<'b, 'tcx>)`.
+pub struct InheritedBuilder<'tcx> {
+    infcx: infer::InferCtxtBuilder<'tcx>,
+    def_id: DefId,
+}
+
+impl Inherited<'_, 'tcx> {
+    pub fn build(tcx: TyCtxt<'tcx>, def_id: DefId) -> InheritedBuilder<'tcx> {
+        let hir_id_root = if def_id.is_local() {
+            let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+            DefId::local(hir_id.owner)
+        } else {
+            def_id
+        };
+
+        InheritedBuilder {
+            infcx: tcx.infer_ctxt().with_fresh_in_progress_tables(hir_id_root),
+            def_id,
+        }
     }
+}
 
-    fn unboxed_closure_upvars(&self,
-                              def_id: ast::DefId,
-                              substs: &Substs<'tcx>)
-                              -> Option<Vec<ty::UnboxedClosureUpvar<'tcx>>>
+impl<'tcx> InheritedBuilder<'tcx> {
+    fn enter<F, R>(&mut self, f: F) -> R
+    where
+        F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R,
     {
-        ty::unboxed_closure_upvars(self, def_id, substs)
+        let def_id = self.def_id;
+        self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id)))
     }
 }
 
-impl<'a, 'tcx> Inherited<'a, 'tcx> {
-    fn new(tcx: &'a ty::ctxt<'tcx>,
-           param_env: ty::ParameterEnvironment<'a, 'tcx>)
-           -> Inherited<'a, 'tcx> {
+impl Inherited<'a, 'tcx> {
+    fn new(infcx: InferCtxt<'a, 'tcx>, def_id: DefId) -> Self {
+        let tcx = infcx.tcx;
+        let item_id = tcx.hir().as_local_hir_id(def_id);
+        let body_id = item_id.and_then(|id| tcx.hir().maybe_body_owned_by(id));
+        let implicit_region_bound = body_id.map(|body_id| {
+            let body = tcx.hir().body(body_id);
+            tcx.mk_region(ty::ReScope(region::Scope {
+                id: body.value.hir_id.local_id,
+                data: region::ScopeData::CallSite
+            }))
+        });
+
         Inherited {
-            infcx: infer::new_infer_ctxt(tcx),
-            locals: RefCell::new(NodeMap::new()),
-            param_env: param_env,
-            node_types: RefCell::new(NodeMap::new()),
-            item_substs: RefCell::new(NodeMap::new()),
-            adjustments: RefCell::new(NodeMap::new()),
-            method_map: RefCell::new(FnvHashMap::new()),
-            object_cast_map: RefCell::new(NodeMap::new()),
-            upvar_borrow_map: RefCell::new(FnvHashMap::new()),
-            unboxed_closures: RefCell::new(DefIdMap::new()),
-            fn_sig_map: RefCell::new(NodeMap::new()),
-            fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()),
+            tables: MaybeInProgressTables {
+                maybe_tables: infcx.in_progress_tables,
+            },
+            infcx,
+            fulfillment_cx: RefCell::new(TraitEngine::new(tcx)),
+            locals: RefCell::new(Default::default()),
+            deferred_sized_obligations: RefCell::new(Vec::new()),
+            deferred_call_resolutions: RefCell::new(Default::default()),
+            deferred_cast_checks: RefCell::new(Vec::new()),
+            deferred_generator_interiors: RefCell::new(Vec::new()),
+            opaque_types: RefCell::new(Default::default()),
+            opaque_types_vars: RefCell::new(Default::default()),
+            implicit_region_bound,
+            body_id,
+        }
+    }
+
+    fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) {
+        debug!("register_predicate({:?})", obligation);
+        if obligation.has_escaping_bound_vars() {
+            span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}",
+                      obligation);
+        }
+        self.fulfillment_cx
+            .borrow_mut()
+            .register_predicate_obligation(self, obligation);
+    }
+
+    fn register_predicates<I>(&self, obligations: I)
+        where I: IntoIterator<Item = traits::PredicateObligation<'tcx>>
+    {
+        for obligation in obligations {
+            self.register_predicate(obligation);
         }
     }
 
+    fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T {
+        self.register_predicates(infer_ok.obligations);
+        infer_ok.value
+    }
+
     fn normalize_associated_types_in<T>(&self,
-                                        typer: &ty::UnboxedClosureTyper<'tcx>,
                                         span: Span,
-                                        body_id: ast::NodeId,
-                                        value: &T)
-                                        -> T
-        where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx>
+                                        body_id: hir::HirId,
+                                        param_env: ty::ParamEnv<'tcx>,
+                                        value: &T) -> T
+        where T : TypeFoldable<'tcx>
     {
-        let mut fulfillment_cx = self.fulfillment_cx.borrow_mut();
-        assoc::normalize_associated_types_in(&self.infcx,
-                                             typer,
-                                             &mut *fulfillment_cx, span,
-                                             body_id,
-                                             value)
+        let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value);
+        self.register_infer_ok_obligations(ok)
     }
+}
 
+struct CheckItemTypesVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
 }
 
-// Used by check_const and check_enum_variants
-pub fn blank_fn_ctxt<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
-                               inh: &'a Inherited<'a, 'tcx>,
-                               rty: ty::FnOutput<'tcx>,
-                               body_id: ast::NodeId)
-                               -> FnCtxt<'a, 'tcx> {
-    FnCtxt {
-        body_id: body_id,
-        writeback_errors: Cell::new(false),
-        err_count_on_creation: ccx.tcx.sess.err_count(),
-        ret_ty: rty,
-        ps: RefCell::new(UnsafetyState::function(ast::Unsafety::Normal, 0)),
-        inh: inh,
-        ccx: ccx
+impl ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> {
+    fn visit_item(&mut self, i: &'tcx hir::Item) {
+        check_item_type(self.tcx, i);
     }
+    fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) { }
+    fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) { }
 }
 
-fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>)
-                                    -> Inherited<'a, 'tcx> {
-    // It's kind of a kludge to manufacture a fake function context
-    // and statement context, but we might as well do write the code only once
-    let param_env = ty::empty_parameter_environment(ccx.tcx);
-    Inherited::new(ccx.tcx, param_env)
+pub fn check_wf_new(tcx: TyCtxt<'_>) {
+    let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx);
+    tcx.hir().krate().par_visit_all_item_likes(&mut visit);
 }
 
-struct CheckItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> }
+fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: DefId) {
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx });
+}
 
-impl<'a, 'tcx, 'v> Visitor<'v> for CheckItemTypesVisitor<'a, 'tcx> {
-    fn visit_item(&mut self, i: &ast::Item) {
-        check_item(self.ccx, i);
-        visit::walk_item(self, i);
-    }
+fn typeck_item_bodies(tcx: TyCtxt<'_>, crate_num: CrateNum) {
+    debug_assert!(crate_num == LOCAL_CRATE);
+    tcx.par_body_owners(|body_owner_def_id| {
+        tcx.ensure().typeck_tables_of(body_owner_def_id);
+    });
+}
+
+fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) {
+    wfcheck::check_item_well_formed(tcx, def_id);
+}
+
+fn check_trait_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) {
+    wfcheck::check_trait_item(tcx, def_id);
+}
+
+fn check_impl_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) {
+    wfcheck::check_impl_item(tcx, def_id);
+}
+
+pub fn provide(providers: &mut Providers<'_>) {
+    method::provide(providers);
+    *providers = Providers {
+        typeck_item_bodies,
+        typeck_tables_of,
+        has_typeck_tables,
+        adt_destructor,
+        used_trait_imports,
+        check_item_well_formed,
+        check_trait_item_well_formed,
+        check_impl_item_well_formed,
+        check_mod_item_types,
+        ..*providers
+    };
+}
+
+fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
+    tcx.calculate_dtor(def_id, &mut dropck::check_drop_impl)
+}
 
-    fn visit_ty(&mut self, t: &ast::Ty) {
-        match t.node {
-            ast::TyFixedLengthVec(_, ref expr) => {
-                check_const_in_type(self.ccx, &**expr, self.ccx.tcx.types.uint);
+/// If this `DefId` is a "primary tables entry", returns
+/// `Some((body_id, header, decl))` with information about
+/// it's body-id, fn-header and fn-decl (if any). Otherwise,
+/// returns `None`.
+///
+/// If this function returns `Some`, then `typeck_tables(def_id)` will
+/// succeed; if it returns `None`, then `typeck_tables(def_id)` may or
+/// may not succeed. In some cases where this function returns `None`
+/// (notably closures), `typeck_tables(def_id)` would wind up
+/// redirecting to the owning function.
+fn primary_body_of(
+    tcx: TyCtxt<'_>,
+    id: hir::HirId,
+) -> Option<(hir::BodyId, Option<&hir::Ty>, Option<&hir::FnHeader>, Option<&hir::FnDecl>)> {
+    match tcx.hir().get(id) {
+        Node::Item(item) => {
+            match item.kind {
+                hir::ItemKind::Const(ref ty, body) |
+                hir::ItemKind::Static(ref ty, _, body) =>
+                    Some((body, Some(ty), None, None)),
+                hir::ItemKind::Fn(ref sig, .., body) =>
+                    Some((body, None, Some(&sig.header), Some(&sig.decl))),
+                _ =>
+                    None,
             }
-            _ => {}
         }
-
-        visit::walk_ty(self, t);
+        Node::TraitItem(item) => {
+            match item.kind {
+                hir::TraitItemKind::Const(ref ty, Some(body)) =>
+                    Some((body, Some(ty), None, None)),
+                hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) =>
+                    Some((body, None, Some(&sig.header), Some(&sig.decl))),
+                _ =>
+                    None,
+            }
+        }
+        Node::ImplItem(item) => {
+            match item.kind {
+                hir::ImplItemKind::Const(ref ty, body) =>
+                    Some((body, Some(ty), None, None)),
+                hir::ImplItemKind::Method(ref sig, body) =>
+                    Some((body, None, Some(&sig.header), Some(&sig.decl))),
+                _ =>
+                    None,
+            }
+        }
+        Node::AnonConst(constant) => Some((constant.body, None, None, None)),
+        _ => None,
     }
 }
 
-pub fn check_item_types(ccx: &CrateCtxt) {
-    let krate = ccx.tcx.map.krate();
-    let mut visit = wf::CheckTypeWellFormedVisitor::new(ccx);
-    visit::walk_crate(&mut visit, krate);
+fn has_typeck_tables(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+    // Closures' tables come from their outermost function,
+    // as they are part of the same "inference environment".
+    let outer_def_id = tcx.closure_base_def_id(def_id);
+    if outer_def_id != def_id {
+        return tcx.has_typeck_tables(outer_def_id);
+    }
+
+    let id = tcx.hir().as_local_hir_id(def_id).unwrap();
+    primary_body_of(tcx, id).is_some()
+}
 
-    // If types are not well-formed, it leads to all manner of errors
-    // downstream, so stop reporting errors at this point.
-    ccx.tcx.sess.abort_if_errors();
+fn used_trait_imports(tcx: TyCtxt<'_>, def_id: DefId) -> &DefIdSet {
+    &*tcx.typeck_tables_of(def_id).used_trait_imports
+}
 
-    let mut visit = CheckItemTypesVisitor { ccx: ccx };
-    visit::walk_crate(&mut visit, krate);
+/// Inspects the substs of opaque types, replacing any inference variables
+/// with proper generic parameter from the identity substs.
+///
+/// This is run after we normalize the function signature, to fix any inference
+/// variables introduced by the projection of associated types. This ensures that
+/// any opaque types used in the signature continue to refer to generic parameters,
+/// allowing them to be considered for defining uses in the function body
+///
+/// For example, consider this code.
+///
+/// ```rust
+/// trait MyTrait {
+///     type MyItem;
+///     fn use_it(self) -> Self::MyItem
+/// }
+/// impl<T, I> MyTrait for T where T: Iterator<Item = I> {
+///     type MyItem = impl Iterator<Item = I>;
+///     fn use_it(self) -> Self::MyItem {
+///         self
+///     }
+/// }
+/// ```
+///
+/// When we normalize the signature of `use_it` from the impl block,
+/// we will normalize `Self::MyItem` to the opaque type `impl Iterator<Item = I>`
+/// However, this projection result may contain inference variables, due
+/// to the way that projection works. We didn't have any inference variables
+/// in the signature to begin with - leaving them in will cause us to incorrectly
+/// conclude that we don't have a defining use of `MyItem`. By mapping inference
+/// variables back to the actual generic parameters, we will correctly see that
+/// we have a defining use of `MyItem`
+fn fixup_opaque_types<'tcx, T>(tcx: TyCtxt<'tcx>, val: &T) -> T where T: TypeFoldable<'tcx> {
+    struct FixupFolder<'tcx> {
+        tcx: TyCtxt<'tcx>
+    }
+
+    impl<'tcx> TypeFolder<'tcx> for FixupFolder<'tcx> {
+        fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+            self.tcx
+        }
+
+        fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+            match ty.kind {
+                ty::Opaque(def_id, substs) => {
+                    debug!("fixup_opaque_types: found type {:?}", ty);
+                    // Here, we replace any inference variables that occur within
+                    // the substs of an opaque type. By definition, any type occuring
+                    // in the substs has a corresponding generic parameter, which is what
+                    // we replace it with.
+                    // This replacement is only run on the function signature, so any
+                    // inference variables that we come across must be the rust of projection
+                    // (there's no other way for a user to get inference variables into
+                    // a function signature).
+                    if ty.needs_infer() {
+                        let new_substs = InternalSubsts::for_item(self.tcx, def_id, |param, _| {
+                            let old_param = substs[param.index as usize];
+                            match old_param.unpack() {
+                                GenericArgKind::Type(old_ty) => {
+                                    if let ty::Infer(_) = old_ty.kind {
+                                        // Replace inference type with a generic parameter
+                                        self.tcx.mk_param_from_def(param)
+                                    } else {
+                                        old_param.fold_with(self)
+                                    }
+                                },
+                                GenericArgKind::Const(old_const) => {
+                                    if let ty::ConstKind::Infer(_) = old_const.val {
+                        // This should never happen - we currently do not support
+                        // 'const projections', e.g.:
+                        // `impl<T: SomeTrait> MyTrait for T where <T as SomeTrait>::MyConst == 25`
+                        // which should be the only way for us to end up with a const inference
+                        // variable after projection. If Rust ever gains support for this kind
+                        // of projection, this should *probably* be changed to
+                        // `self.tcx.mk_param_from_def(param)`
+                                        bug!("Found infer const: `{:?}` in opaque type: {:?}",
+                                             old_const, ty);
+                                    } else {
+                                        old_param.fold_with(self)
+                                    }
+                                }
+                                GenericArgKind::Lifetime(old_region) => {
+                                    if let RegionKind::ReVar(_) = old_region {
+                                        self.tcx.mk_param_from_def(param)
+                                    } else {
+                                        old_param.fold_with(self)
+                                    }
+                                }
+                            }
+                        });
+                        let new_ty = self.tcx.mk_opaque(def_id, new_substs);
+                        debug!("fixup_opaque_types: new type: {:?}", new_ty);
+                        new_ty
+                    } else {
+                        ty
+                    }
+                },
+                _ => ty.super_fold_with(self)
+            }
+        }
+    }
 
-    ccx.tcx.sess.abort_if_errors();
+    debug!("fixup_opaque_types({:?})", val);
+    val.fold_with(&mut FixupFolder { tcx })
 }
 
-fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                           decl: &ast::FnDecl,
-                           body: &ast::Block,
-                           id: ast::NodeId,
-                           raw_fty: Ty<'tcx>,
-                           param_env: ty::ParameterEnvironment<'a, 'tcx>) {
-    match raw_fty.sty {
-        ty::ty_bare_fn(_, ref fn_ty) => {
-            let inh = Inherited::new(ccx.tcx, param_env);
-
-            // Compute the fty from point of view of inside fn.
-            let fn_sig =
-                fn_ty.sig.subst(ccx.tcx, &inh.param_env.free_substs);
+fn typeck_tables_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TypeckTables<'_> {
+    // Closures' tables come from their outermost function,
+    // as they are part of the same "inference environment".
+    let outer_def_id = tcx.closure_base_def_id(def_id);
+    if outer_def_id != def_id {
+        return tcx.typeck_tables_of(outer_def_id);
+    }
+
+    let id = tcx.hir().as_local_hir_id(def_id).unwrap();
+    let span = tcx.hir().span(id);
+
+    // Figure out what primary body this item has.
+    let (body_id, body_ty, fn_header, fn_decl) = primary_body_of(tcx, id)
+        .unwrap_or_else(|| {
+            span_bug!(span, "can't type-check body of {:?}", def_id);
+        });
+    let body = tcx.hir().body(body_id);
+
+    let tables = Inherited::build(tcx, def_id).enter(|inh| {
+        let param_env = tcx.param_env(def_id);
+        let fcx = if let (Some(header), Some(decl)) = (fn_header, fn_decl) {
+            let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
+                let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
+                AstConv::ty_of_fn(&fcx, header.unsafety, header.abi, decl)
+            } else {
+                tcx.fn_sig(def_id)
+            };
+
+            check_abi(tcx, span, fn_sig.abi());
+
+            // Compute the fty from point of view of inside the fn.
             let fn_sig =
-                liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig);
+                tcx.liberate_late_bound_regions(def_id, &fn_sig);
             let fn_sig =
-                inh.normalize_associated_types_in(&inh.param_env, body.span, body.id, &fn_sig);
+                inh.normalize_associated_types_in(body.value.span,
+                                                  body_id.hir_id,
+                                                  param_env,
+                                                  &fn_sig);
+
+            let fn_sig = fixup_opaque_types(tcx, &fn_sig);
+
+            let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None).0;
+            fcx
+        } else {
+            let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
+            let expected_type = body_ty.and_then(|ty| match ty.kind {
+                hir::TyKind::Infer => Some(AstConv::ast_ty_to_ty(&fcx, ty)),
+                _ => None
+            }).unwrap_or_else(|| tcx.type_of(def_id));
+            let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type);
+            fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
+
+            let revealed_ty = if tcx.features().impl_trait_in_bindings {
+                fcx.instantiate_opaque_types_from_value(
+                    id,
+                    &expected_type,
+                    body.value.span,
+                )
+            } else {
+                expected_type
+            };
+
+            // Gather locals in statics (because of block expressions).
+            GatherLocalsVisitor { fcx: &fcx, parent_id: id, }.visit_body(body);
+
+            fcx.check_expr_coercable_to_type(&body.value, revealed_ty);
 
-            let fcx = check_fn(ccx, fn_ty.unsafety, id, &fn_sig,
-                               decl, id, body, &inh);
+            fcx.write_ty(id, revealed_ty);
 
-            vtable::select_all_fcx_obligations_or_error(&fcx);
-            upvar::closure_analyze_fn(&fcx, id, decl, body);
-            regionck::regionck_fn(&fcx, id, decl, body);
-            writeback::resolve_type_vars_in_fn(&fcx, decl, body);
+            fcx
+        };
+
+        // All type checking constraints were added, try to fallback unsolved variables.
+        fcx.select_obligations_where_possible(false, |_| {});
+        let mut fallback_has_occurred = false;
+
+        // We do fallback in two passes, to try to generate
+        // better error messages.
+        // The first time, we do *not* replace opaque types.
+        for ty in &fcx.unsolved_variables() {
+            fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::NoOpaque);
+        }
+        // We now see if we can make progress. This might
+        // cause us to unify inference variables for opaque types,
+        // since we may have unified some other type variables
+        // during the first phase of fallback.
+        // This means that we only replace inference variables with their underlying
+        // opaque types as a last resort.
+        //
+        // In code like this:
+        //
+        // ```rust
+        // type MyType = impl Copy;
+        // fn produce() -> MyType { true }
+        // fn bad_produce() -> MyType { panic!() }
+        // ```
+        //
+        // we want to unify the opaque inference variable in `bad_produce`
+        // with the diverging fallback for `panic!` (e.g. `()` or `!`).
+        // This will produce a nice error message about conflicting concrete
+        // types for `MyType`.
+        //
+        // If we had tried to fallback the opaque inference variable to `MyType`,
+        // we will generate a confusing type-check error that does not explicitly
+        // refer to opaque types.
+        fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
+
+        // We now run fallback again, but this time we allow it to replace
+        // unconstrained opaque type variables, in addition to performing
+        // other kinds of fallback.
+        for ty in &fcx.unsolved_variables() {
+            fallback_has_occurred |= fcx.fallback_if_possible(ty, FallbackMode::All);
+        }
+
+        // See if we can make any more progress.
+        fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
+
+        // Even though coercion casts provide type hints, we check casts after fallback for
+        // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
+        fcx.check_casts();
+
+        // Closure and generator analysis may run after fallback
+        // because they don't constrain other type variables.
+        fcx.closure_analyze(body);
+        assert!(fcx.deferred_call_resolutions.borrow().is_empty());
+        fcx.resolve_generator_interiors(def_id);
+
+        for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
+            let ty = fcx.normalize_ty(span, ty);
+            fcx.require_type_is_sized(ty, span, code);
         }
-        _ => ccx.tcx.sess.impossible_case(body.span,
-                                 "check_bare_fn: function type expected")
+        fcx.select_all_obligations_or_error();
+
+        if fn_decl.is_some() {
+            fcx.regionck_fn(id, body);
+        } else {
+            fcx.regionck_expr(body);
+        }
+
+        fcx.resolve_type_vars_in_body(body)
+    });
+
+    // Consistency check our TypeckTables instance can hold all ItemLocalIds
+    // it will need to hold.
+    assert_eq!(tables.local_id_root, Some(DefId::local(id.owner)));
+
+    tables
+}
+
+fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) {
+    if !tcx.sess.target.target.is_abi_supported(abi) {
+        struct_span_err!(tcx.sess, span, E0570,
+            "The ABI `{}` is not supported for the current target", abi).emit()
     }
 }
 
-struct GatherLocalsVisitor<'a, 'tcx: 'a> {
-    fcx: &'a FnCtxt<'a, 'tcx>
+struct GatherLocalsVisitor<'a, 'tcx> {
+    fcx: &'a FnCtxt<'a, 'tcx>,
+    parent_id: hir::HirId,
 }
 
 impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
-    fn assign(&mut self, _span: Span, nid: ast::NodeId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
+    fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
         match ty_opt {
             None => {
                 // infer the variable's type
-                let var_ty = self.fcx.infcx().next_ty_var();
-                self.fcx.inh.locals.borrow_mut().insert(nid, var_ty);
+                let var_ty = self.fcx.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::TypeInference,
+                    span,
+                });
+                self.fcx.locals.borrow_mut().insert(nid, LocalTy {
+                    decl_ty: var_ty,
+                    revealed_ty: var_ty
+                });
                 var_ty
             }
             Some(typ) => {
                 // take type that the user specified
-                self.fcx.inh.locals.borrow_mut().insert(nid, typ);
-                typ
+                self.fcx.locals.borrow_mut().insert(nid, typ);
+                typ.revealed_ty
             }
         }
     }
 }
 
-impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> {
+impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
+    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+        NestedVisitorMap::None
+    }
+
     // Add explicitly-declared locals.
-    fn visit_local(&mut self, local: &ast::Local) {
-        let o_ty = match local.ty {
-            Some(ref ty) => Some(self.fcx.to_ty(&**ty)),
-            None => None
+    fn visit_local(&mut self, local: &'tcx hir::Local) {
+        let local_ty = match local.ty {
+            Some(ref ty) => {
+                let o_ty = self.fcx.to_ty(&ty);
+
+                let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings {
+                    self.fcx.instantiate_opaque_types_from_value(
+                        self.parent_id,
+                        &o_ty,
+                        ty.span,
+                    )
+                } else {
+                    o_ty
+                };
+
+                let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(
+                    &UserType::Ty(revealed_ty)
+                );
+                debug!("visit_local: ty.hir_id={:?} o_ty={:?} revealed_ty={:?} c_ty={:?}",
+                       ty.hir_id, o_ty, revealed_ty, c_ty);
+                self.fcx.tables.borrow_mut().user_provided_types_mut().insert(ty.hir_id, c_ty);
+
+                Some(LocalTy { decl_ty: o_ty, revealed_ty })
+            },
+            None => None,
         };
-        self.assign(local.span, local.id, o_ty);
-        debug!("Local variable {} is assigned type {}",
-               self.fcx.pat_to_string(&*local.pat),
-               self.fcx.infcx().ty_to_string(
-                   self.fcx.inh.locals.borrow()[local.id].clone()));
-        visit::walk_local(self, local);
+        self.assign(local.span, local.hir_id, local_ty);
+
+        debug!("local variable {:?} is assigned type {}",
+               local.pat,
+               self.fcx.ty_to_string(
+                   self.fcx.locals.borrow().get(&local.hir_id).unwrap().clone().decl_ty));
+        intravisit::walk_local(self, local);
     }
 
     // Add pattern bindings.
-    fn visit_pat(&mut self, p: &ast::Pat) {
-        if let ast::PatIdent(_, ref path1, _) = p.node {
-            if pat_util::pat_is_binding(&self.fcx.ccx.tcx.def_map, p) {
-                let var_ty = self.assign(p.span, p.id, None);
+    fn visit_pat(&mut self, p: &'tcx hir::Pat) {
+        if let PatKind::Binding(_, _, ident, _) = p.kind {
+            let var_ty = self.assign(p.span, p.hir_id, None);
 
+            if !self.fcx.tcx.features().unsized_locals {
                 self.fcx.require_type_is_sized(var_ty, p.span,
-                                               traits::VariableType(p.id));
-
-                debug!("Pattern binding {} is assigned to {} with type {}",
-                       token::get_ident(path1.node),
-                       self.fcx.infcx().ty_to_string(
-                           self.fcx.inh.locals.borrow()[p.id].clone()),
-                       var_ty.repr(self.fcx.tcx()));
+                                               traits::VariableType(p.hir_id));
             }
+
+            debug!("pattern binding {} is assigned to {} with type {:?}",
+                   ident,
+                   self.fcx.ty_to_string(
+                       self.fcx.locals.borrow().get(&p.hir_id).unwrap().clone().decl_ty),
+                   var_ty);
         }
-        visit::walk_pat(self, p);
+        intravisit::walk_pat(self, p);
     }
 
-    fn visit_block(&mut self, b: &ast::Block) {
-        // non-obvious: the `blk` variable maps to region lb, so
-        // we have to keep this up-to-date.  This
-        // is... unfortunate.  It'd be nice to not need this.
-        visit::walk_block(self, b);
-    }
+    // Don't descend into the bodies of nested closures
+    fn visit_fn(
+        &mut self,
+        _: intravisit::FnKind<'tcx>,
+        _: &'tcx hir::FnDecl,
+        _: hir::BodyId,
+        _: Span,
+        _: hir::HirId,
+    ) { }
+}
 
-    // Since an expr occurs as part of the type fixed size arrays we
-    // need to record the type for that node
-    fn visit_ty(&mut self, t: &ast::Ty) {
-        match t.node {
-            ast::TyFixedLengthVec(ref ty, ref count_expr) => {
-                self.visit_ty(&**ty);
-                check_expr_with_hint(self.fcx, &**count_expr, self.fcx.tcx().types.uint);
-            }
-            _ => visit::walk_ty(self, t)
-        }
-    }
+/// When `check_fn` is invoked on a generator (i.e., a body that
+/// includes yield), it returns back some information about the yield
+/// points.
+struct GeneratorTypes<'tcx> {
+    /// Type of value that is yielded.
+    yield_ty: Ty<'tcx>,
 
-    // Don't descend into fns and items
-    fn visit_fn(&mut self, _: visit::FnKind<'v>, _: &'v ast::FnDecl,
-                _: &'v ast::Block, _: Span, _: ast::NodeId) { }
-    fn visit_item(&mut self, _: &ast::Item) { }
+    /// Types that are captured (see `GeneratorInterior` for more).
+    interior: Ty<'tcx>,
 
+    /// Indicates if the generator is movable or static (immovable).
+    movability: hir::Movability,
 }
 
-/// Helper used by check_bare_fn and check_expr_fn. Does the grungy work of checking a function
+/// Helper used for fns and closures. Does the grungy work of checking a function
 /// body and returns the function context used for that purpose, since in the case of a fn item
 /// there is still a bit more to do.
 ///
 /// * ...
 /// * inherited: other fields inherited from the enclosing fn (if any)
-fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
-                      unsafety: ast::Unsafety,
-                      unsafety_id: ast::NodeId,
-                      fn_sig: &ty::FnSig<'tcx>,
-                      decl: &ast::FnDecl,
-                      fn_id: ast::NodeId,
-                      body: &ast::Block,
-                      inherited: &'a Inherited<'a, 'tcx>)
-                      -> FnCtxt<'a, 'tcx>
-{
-    let tcx = ccx.tcx;
-    let err_count_on_creation = tcx.sess.err_count();
-
-    let arg_tys = &fn_sig.inputs[];
-    let ret_ty = fn_sig.output;
-
-    debug!("check_fn(arg_tys={}, ret_ty={}, fn_id={})",
-           arg_tys.repr(tcx),
-           ret_ty.repr(tcx),
-           fn_id);
+fn check_fn<'a, 'tcx>(
+    inherited: &'a Inherited<'a, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    fn_sig: ty::FnSig<'tcx>,
+    decl: &'tcx hir::FnDecl,
+    fn_id: hir::HirId,
+    body: &'tcx hir::Body,
+    can_be_generator: Option<hir::Movability>,
+) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
+    let mut fn_sig = fn_sig.clone();
+
+    debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env);
 
     // Create the function context.  This is either derived from scratch or,
-    // in the case of function expressions, based on the outer context.
-    let fcx = FnCtxt {
-        body_id: body.id,
-        writeback_errors: Cell::new(false),
-        err_count_on_creation: err_count_on_creation,
-        ret_ty: ret_ty,
-        ps: RefCell::new(UnsafetyState::function(unsafety, unsafety_id)),
-        inh: inherited,
-        ccx: ccx
-    };
-
-    // Remember return type so that regionck can access it later.
-    let mut fn_sig_tys: Vec<Ty> =
-        arg_tys.iter()
-        .map(|&ty| ty)
-        .collect();
-
-    if let ty::FnConverging(ret_ty) = ret_ty {
-        fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
-        fn_sig_tys.push(ret_ty);
+    // in the case of closures, based on the outer context.
+    let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
+    *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
+
+    let declared_ret_ty = fn_sig.output();
+    fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
+    let revealed_ret_ty = fcx.instantiate_opaque_types_from_value(
+        fn_id,
+        &declared_ret_ty,
+        decl.output.span(),
+    );
+    debug!("check_fn: declared_ret_ty: {}, revealed_ret_ty: {}", declared_ret_ty, revealed_ret_ty);
+    fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty)));
+    fn_sig = fcx.tcx.mk_fn_sig(
+        fn_sig.inputs().iter().cloned(),
+        revealed_ret_ty,
+        fn_sig.c_variadic,
+        fn_sig.unsafety,
+        fn_sig.abi
+    );
+
+    let span = body.value.span;
+
+    fn_maybe_err(fcx.tcx, span, fn_sig.abi);
+
+    if body.generator_kind.is_some() && can_be_generator.is_some() {
+        let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
+            kind: TypeVariableOriginKind::TypeInference,
+            span,
+        });
+        fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
+        fcx.yield_ty = Some(yield_ty);
     }
 
-    debug!("fn-sig-map: fn_id={} fn_sig_tys={}",
-           fn_id,
-           fn_sig_tys.repr(tcx));
+    let outer_def_id = fcx.tcx.closure_base_def_id(fcx.tcx.hir().local_def_id(fn_id));
+    let outer_hir_id = fcx.tcx.hir().as_local_hir_id(outer_def_id).unwrap();
+    GatherLocalsVisitor { fcx: &fcx, parent_id: outer_hir_id, }.visit_body(body);
 
-    inherited.fn_sig_map.borrow_mut().insert(fn_id, fn_sig_tys);
+    // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
+    // (as it's created inside the body itself, not passed in from outside).
+    let maybe_va_list = if fn_sig.c_variadic {
+        let va_list_did = fcx.tcx.require_lang_item(
+            lang_items::VaListTypeLangItem,
+            Some(body.params.last().unwrap().span),
+        );
+        let region = fcx.tcx.mk_region(ty::ReScope(region::Scope {
+            id: body.value.hir_id.local_id,
+            data: region::ScopeData::CallSite
+        }));
+
+        Some(fcx.tcx.type_of(va_list_did).subst(fcx.tcx, &[region.into()]))
+    } else {
+        None
+    };
 
+    // Add formal parameters.
+    for (param_ty, param) in
+        fn_sig.inputs().iter().copied()
+            .chain(maybe_va_list)
+            .zip(&body.params)
     {
-        let mut visit = GatherLocalsVisitor { fcx: &fcx, };
-
-        // Add formal parameters.
-        for (arg_ty, input) in arg_tys.iter().zip(decl.inputs.iter()) {
-            // Create type variables for each argument.
-            pat_util::pat_bindings(
-                &tcx.def_map,
-                &*input.pat,
-                |_bm, pat_id, sp, _path| {
-                    let var_ty = visit.assign(sp, pat_id, None);
-                    fcx.require_type_is_sized(var_ty, sp,
-                                              traits::VariableType(pat_id));
-                });
+        // Check the pattern.
+        fcx.check_pat_top(&param.pat, param_ty, None);
 
-            // Check the pattern.
-            let pcx = pat_ctxt {
-                fcx: &fcx,
-                map: pat_id_map(&tcx.def_map, &*input.pat),
-            };
-            _match::check_pat(&pcx, &*input.pat, *arg_ty);
+        // Check that argument is Sized.
+        // The check for a non-trivial pattern is a hack to avoid duplicate warnings
+        // for simple cases like `fn foo(x: Trait)`,
+        // where we would error once on the parameter as a whole, and once on the binding `x`.
+        if param.pat.simple_ident().is_none() && !fcx.tcx.features().unsized_locals {
+            fcx.require_type_is_sized(param_ty, decl.output.span(), traits::SizedArgumentType);
         }
 
-        visit.visit_block(body);
-    }
-
-    check_block_with_expected(&fcx, body, match ret_ty {
-        ty::FnConverging(result_type) => ExpectHasType(result_type),
-        ty::FnDiverging => NoExpectation
-    });
-
-    for (input, arg) in decl.inputs.iter().zip(arg_tys.iter()) {
-        fcx.write_ty(input.id, *arg);
+        fcx.write_ty(param.hir_id, param_ty);
     }
 
-    fcx
-}
+    inherited.tables.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
 
-pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
-    let tcx = ccx.tcx;
+    fcx.check_return_expr(&body.value);
 
-    check_representable(tcx, span, id, "struct");
-    check_instantiable(tcx, span, id);
+    // We insert the deferred_generator_interiors entry after visiting the body.
+    // This ensures that all nested generators appear before the entry of this generator.
+    // resolve_generator_interiors relies on this property.
+    let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) {
+        let interior = fcx.next_ty_var(TypeVariableOrigin {
+            kind: TypeVariableOriginKind::MiscVariable,
+            span,
+        });
+        fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind));
+        Some(GeneratorTypes {
+            yield_ty: fcx.yield_ty.unwrap(),
+            interior,
+            movability: can_be_generator.unwrap(),
+        })
+    } else {
+        None
+    };
 
-    if ty::lookup_simd(tcx, local_def(id)) {
-        check_simd(tcx, span, id);
+    // Finalize the return check by taking the LUB of the return types
+    // we saw and assigning it to the expected return type. This isn't
+    // really expected to fail, since the coercions would have failed
+    // earlier when trying to find a LUB.
+    //
+    // However, the behavior around `!` is sort of complex. In the
+    // event that the `actual_return_ty` comes back as `!`, that
+    // indicates that the fn either does not return or "returns" only
+    // values of type `!`. In this case, if there is an expected
+    // return type that is *not* `!`, that should be ok. But if the
+    // return type is being inferred, we want to "fallback" to `!`:
+    //
+    //     let x = move || panic!();
+    //
+    // To allow for that, I am creating a type variable with diverging
+    // fallback. This was deemed ever so slightly better than unifying
+    // the return value with `!` because it allows for the caller to
+    // make more assumptions about the return type (e.g., they could do
+    //
+    //     let y: Option<u32> = Some(x());
+    //
+    // which would then cause this return type to become `u32`, not
+    // `!`).
+    let coercion = fcx.ret_coercion.take().unwrap().into_inner();
+    let mut actual_return_ty = coercion.complete(&fcx);
+    if actual_return_ty.is_never() {
+        actual_return_ty = fcx.next_diverging_ty_var(
+            TypeVariableOrigin {
+                kind: TypeVariableOriginKind::DivergingFn,
+                span,
+            },
+        );
     }
-}
-
-pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
-    debug!("check_item(it.id={}, it.ident={})",
-           it.id,
-           ty::item_path_str(ccx.tcx, local_def(it.id)));
-    let _indenter = indenter();
-
-    match it.node {
-      ast::ItemStatic(_, _, ref e) |
-      ast::ItemConst(_, ref e) => check_const(ccx, it.span, &**e, it.id),
-      ast::ItemEnum(ref enum_definition, _) => {
-        check_enum_variants(ccx,
-                            it.span,
-                            &enum_definition.variants[],
-                            it.id);
-      }
-      ast::ItemFn(ref decl, _, _, _, ref body) => {
-        let fn_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
-        let param_env = ParameterEnvironment::for_item(ccx.tcx, it.id);
-        check_bare_fn(ccx, &**decl, &**body, it.id, fn_pty.ty, param_env);
-      }
-      ast::ItemImpl(_, _, _, _, _, ref impl_items) => {
-        debug!("ItemImpl {} with id {}", token::get_ident(it.ident), it.id);
-
-        let impl_pty = ty::lookup_item_type(ccx.tcx, ast_util::local_def(it.id));
-
-          match ty::impl_trait_ref(ccx.tcx, local_def(it.id)) {
-              Some(impl_trait_ref) => {
-                check_impl_items_against_trait(ccx,
-                                               it.span,
-                                               &*impl_trait_ref,
-                                               impl_items.as_slice());
-              }
-              None => { }
-          }
-
-        for impl_item in impl_items.iter() {
-            match *impl_item {
-                ast::MethodImplItem(ref m) => {
-                    check_method_body(ccx, &impl_pty.generics, &**m);
-                }
-                ast::TypeImplItem(_) => {
-                    // Nothing to do here.
-                }
-            }
-        }
-
-      }
-      ast::ItemTrait(_, _, _, ref trait_methods) => {
-        let trait_def = ty::lookup_trait_def(ccx.tcx, local_def(it.id));
-        for trait_method in trait_methods.iter() {
-            match *trait_method {
-                RequiredMethod(..) => {
-                    // Nothing to do, since required methods don't have
-                    // bodies to check.
-                }
-                ProvidedMethod(ref m) => {
-                    check_method_body(ccx, &trait_def.generics, &**m);
-                }
-                TypeTraitItem(_) => {
-                    // Nothing to do.
-                }
-            }
-        }
-      }
-      ast::ItemStruct(..) => {
-        check_struct(ccx, it.id, it.span);
-      }
-      ast::ItemTy(ref t, ref generics) => {
-        let pty_ty = ty::node_id_to_type(ccx.tcx, it.id);
-        check_bounds_are_used(ccx, t.span, &generics.ty_params, pty_ty);
-      }
-      ast::ItemForeignMod(ref m) => {
-        if m.abi == abi::RustIntrinsic {
-            for item in m.items.iter() {
-                check_intrinsic_type(ccx, &**item);
-            }
-        } else {
-            for item in m.items.iter() {
-                let pty = ty::lookup_item_type(ccx.tcx, local_def(item.id));
-                if !pty.generics.types.is_empty() {
-                    span_err!(ccx.tcx.sess, item.span, E0044,
-                        "foreign items may not have type parameters");
-                }
-
-                if let ast::ForeignItemFn(ref fn_decl, _) = item.node {
-                    if fn_decl.variadic && m.abi != abi::C {
-                        span_err!(ccx.tcx.sess, item.span, E0045,
-                                  "variadic function must have C calling convention");
-                    }
-                }
+    fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty);
+
+    // Check that the main return type implements the termination trait.
+    if let Some(term_id) = fcx.tcx.lang_items().termination() {
+        if let Some((def_id, EntryFnType::Main)) = fcx.tcx.entry_fn(LOCAL_CRATE) {
+            let main_id = fcx.tcx.hir().as_local_hir_id(def_id).unwrap();
+            if main_id == fn_id {
+                let substs = fcx.tcx.mk_substs_trait(declared_ret_ty, &[]);
+                let trait_ref = ty::TraitRef::new(term_id, substs);
+                let return_ty_span = decl.output.span();
+                let cause = traits::ObligationCause::new(
+                    return_ty_span, fn_id, ObligationCauseCode::MainFunctionType);
+
+                inherited.register_predicate(
+                    traits::Obligation::new(
+                        cause, param_env, trait_ref.to_predicate()));
             }
         }
-      }
-      _ => {/* nothing to do */ }
     }
-}
 
-/// Type checks a method body.
-///
-/// # Parameters
-///
-/// * `item_generics`: generics defined on the impl/trait that contains
-///   the method
-/// * `self_bound`: bound for the `Self` type parameter, if any
-/// * `method`: the method definition
-fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                               item_generics: &ty::Generics<'tcx>,
-                               method: &ast::Method) {
-    debug!("check_method_body(item_generics={}, method.id={})",
-            item_generics.repr(ccx.tcx),
-            method.id);
-    let param_env = ParameterEnvironment::for_item(ccx.tcx, method.id);
-
-    let fty = ty::node_id_to_type(ccx.tcx, method.id);
-    debug!("check_method_body: fty={}", fty.repr(ccx.tcx));
-
-    check_bare_fn(ccx,
-                  &*method.pe_fn_decl(),
-                  &*method.pe_body(),
-                  method.id,
-                  fty,
-                  param_env);
-}
+    // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
+    if let Some(panic_impl_did) = fcx.tcx.lang_items().panic_impl() {
+        if panic_impl_did == fcx.tcx.hir().local_def_id(fn_id) {
+            if let Some(panic_info_did) = fcx.tcx.lang_items().panic_info() {
+                if declared_ret_ty.kind != ty::Never {
+                    fcx.tcx.sess.span_err(
+                        decl.output.span(),
+                        "return type should be `!`",
+                    );
+                }
 
-fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                                            impl_span: Span,
-                                            impl_trait_ref: &ty::TraitRef<'tcx>,
-                                            impl_items: &[ast::ImplItem]) {
-    // Locate trait methods
-    let tcx = ccx.tcx;
-    let trait_items = ty::trait_items(tcx, impl_trait_ref.def_id);
+                let inputs = fn_sig.inputs();
+                let span = fcx.tcx.hir().span(fn_id);
+                if inputs.len() == 1 {
+                    let arg_is_panic_info = match inputs[0].kind {
+                        ty::Ref(region, ty, mutbl) => match ty.kind {
+                            ty::Adt(ref adt, _) => {
+                                adt.did == panic_info_did &&
+                                    mutbl == hir::Mutability::Immutable &&
+                                    *region != RegionKind::ReStatic
+                            },
+                            _ => false,
+                        },
+                        _ => false,
+                    };
 
-    // Check existing impl methods to see if they are both present in trait
-    // and compatible with trait signature
-    for impl_item in impl_items.iter() {
-        match *impl_item {
-            ast::MethodImplItem(ref impl_method) => {
-                let impl_method_def_id = local_def(impl_method.id);
-                let impl_item_ty = ty::impl_or_trait_item(ccx.tcx,
-                                                          impl_method_def_id);
-
-                // If this is an impl of a trait method, find the
-                // corresponding method definition in the trait.
-                let opt_trait_method_ty =
-                    trait_items.iter()
-                               .find(|ti| ti.name() == impl_item_ty.name());
-                match opt_trait_method_ty {
-                    Some(trait_method_ty) => {
-                        match (trait_method_ty, &impl_item_ty) {
-                            (&ty::MethodTraitItem(ref trait_method_ty),
-                             &ty::MethodTraitItem(ref impl_method_ty)) => {
-                                compare_impl_method(ccx.tcx,
-                                                    &**impl_method_ty,
-                                                    impl_method.span,
-                                                    impl_method.pe_body().id,
-                                                    &**trait_method_ty,
-                                                    &*impl_trait_ref);
-                            }
-                            _ => {
-                                // This is span_bug as it should have already been
-                                // caught in resolve.
-                                tcx.sess.span_bug(
-                                    impl_method.span,
-                                    format!("item `{}` is of a different kind from its trait `{}`",
-                                            token::get_name(impl_item_ty.name()),
-                                            impl_trait_ref.repr(tcx)).as_slice());
-                            }
-                        }
-                    }
-                    None => {
-                        // This is span_bug as it should have already been
-                        // caught in resolve.
-                        tcx.sess.span_bug(
-                            impl_method.span,
-                            format!("method `{}` is not a member of trait `{}`",
-                                    token::get_name(impl_item_ty.name()),
-                                    impl_trait_ref.repr(tcx)).as_slice());
+                    if !arg_is_panic_info {
+                        fcx.tcx.sess.span_err(
+                            decl.inputs[0].span,
+                            "argument should be `&PanicInfo`",
+                        );
                     }
-                }
-            }
-            ast::TypeImplItem(ref typedef) => {
-                let typedef_def_id = local_def(typedef.id);
-                let typedef_ty = ty::impl_or_trait_item(ccx.tcx,
-                                                        typedef_def_id);
-
-                // If this is an impl of an associated type, find the
-                // corresponding type definition in the trait.
-                let opt_associated_type =
-                    trait_items.iter()
-                               .find(|ti| ti.name() == typedef_ty.name());
-                match opt_associated_type {
-                    Some(associated_type) => {
-                        match (associated_type, &typedef_ty) {
-                            (&ty::TypeTraitItem(_), &ty::TypeTraitItem(_)) => {}
-                            _ => {
-                                // This is `span_bug` as it should have
-                                // already been caught in resolve.
-                                tcx.sess.span_bug(
-                                    typedef.span,
-                                    format!("item `{}` is of a different kind from its trait `{}`",
-                                            token::get_name(typedef_ty.name()),
-                                            impl_trait_ref.repr(tcx)).as_slice());
+
+                    if let Node::Item(item) = fcx.tcx.hir().get(fn_id) {
+                        if let ItemKind::Fn(_, ref generics, _) = item.kind {
+                            if !generics.params.is_empty() {
+                                fcx.tcx.sess.span_err(
+                                    span,
+                                    "should have no type parameters",
+                                );
                             }
                         }
                     }
-                    None => {
-                        // This is `span_bug` as it should have already been
-                        // caught in resolve.
-                        tcx.sess.span_bug(
-                            typedef.span,
-                            format!(
-                                "associated type `{}` is not a member of \
-                                 trait `{}`",
-                                token::get_name(typedef_ty.name()),
-                                impl_trait_ref.repr(tcx)).as_slice());
-                    }
+                } else {
+                    let span = fcx.tcx.sess.source_map().def_span(span);
+                    fcx.tcx.sess.span_err(span, "function should have one argument");
                 }
+            } else {
+                fcx.tcx.sess.err("language item required, but not found: `panic_info`");
             }
         }
     }
 
-    // Check for missing items from trait
-    let provided_methods = ty::provided_trait_methods(tcx, impl_trait_ref.def_id);
-    let mut missing_methods = Vec::new();
-    for trait_item in trait_items.iter() {
-        match *trait_item {
-            ty::MethodTraitItem(ref trait_method) => {
-                let is_implemented =
-                    impl_items.iter().any(|ii| {
-                        match *ii {
-                            ast::MethodImplItem(ref m) => {
-                                m.pe_ident().name == trait_method.name
-                            }
-                            ast::TypeImplItem(_) => false,
-                        }
-                    });
-                let is_provided =
-                    provided_methods.iter().any(|m| m.name == trait_method.name);
-                if !is_implemented && !is_provided {
-                    missing_methods.push(format!("`{}`", token::get_name(trait_method.name)));
+    // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !`
+    if let Some(alloc_error_handler_did) = fcx.tcx.lang_items().oom() {
+        if alloc_error_handler_did == fcx.tcx.hir().local_def_id(fn_id) {
+            if let Some(alloc_layout_did) = fcx.tcx.lang_items().alloc_layout() {
+                if declared_ret_ty.kind != ty::Never {
+                    fcx.tcx.sess.span_err(
+                        decl.output.span(),
+                        "return type should be `!`",
+                    );
                 }
-            }
-            ty::TypeTraitItem(ref associated_type) => {
-                let is_implemented = impl_items.iter().any(|ii| {
-                    match *ii {
-                        ast::TypeImplItem(ref typedef) => {
-                            typedef.ident.name == associated_type.name
+
+                let inputs = fn_sig.inputs();
+                let span = fcx.tcx.hir().span(fn_id);
+                if inputs.len() == 1 {
+                    let arg_is_alloc_layout = match inputs[0].kind {
+                        ty::Adt(ref adt, _) => {
+                            adt.did == alloc_layout_did
+                        },
+                        _ => false,
+                    };
+
+                    if !arg_is_alloc_layout {
+                        fcx.tcx.sess.span_err(
+                            decl.inputs[0].span,
+                            "argument should be `Layout`",
+                        );
+                    }
+
+                    if let Node::Item(item) = fcx.tcx.hir().get(fn_id) {
+                        if let ItemKind::Fn(_, ref generics, _) = item.kind {
+                            if !generics.params.is_empty() {
+                                fcx.tcx.sess.span_err(
+                                    span,
+                                    "`#[alloc_error_handler]` function should have no type \
+                                     parameters",
+                                );
+                            }
                         }
-                        ast::MethodImplItem(_) => false,
                     }
-                });
-                if !is_implemented {
-                    missing_methods.push(format!("`{}`", token::get_name(associated_type.name)));
+                } else {
+                    let span = fcx.tcx.sess.source_map().def_span(span);
+                    fcx.tcx.sess.span_err(span, "function should have one argument");
                 }
+            } else {
+                fcx.tcx.sess.err("language item required, but not found: `alloc_layout`");
             }
         }
     }
 
-    if !missing_methods.is_empty() {
-        span_err!(tcx.sess, impl_span, E0046,
-            "not all trait items implemented, missing: {}",
-            missing_methods.connect(", "));
-    }
+    (fcx, gen_ty)
 }
 
-/// Checks that a method from an impl conforms to the signature of
-/// the same method as declared in the trait.
-///
-/// # Parameters
-///
-/// - impl_generics: the generics declared on the impl itself (not the method!)
-/// - impl_m: type of the method we are checking
-/// - impl_m_span: span to use for reporting errors
-/// - impl_m_body_id: id of the method body
-/// - trait_m: the method in the trait
-/// - trait_to_impl_substs: the substitutions used on the type of the trait
-fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
-                             impl_m: &ty::Method<'tcx>,
-                             impl_m_span: Span,
-                             impl_m_body_id: ast::NodeId,
-                             trait_m: &ty::Method<'tcx>,
-                             impl_trait_ref: &ty::TraitRef<'tcx>) {
-    debug!("compare_impl_method(impl_trait_ref={})",
-           impl_trait_ref.repr(tcx));
-
-    debug!("impl_trait_ref (liberated) = {}",
-           impl_trait_ref.repr(tcx));
-
-    let infcx = infer::new_infer_ctxt(tcx);
-    let mut fulfillment_cx = traits::FulfillmentContext::new();
-
-    let trait_to_impl_substs = &impl_trait_ref.substs;
-
-    // Try to give more informative error messages about self typing
-    // mismatches.  Note that any mismatch will also be detected
-    // below, where we construct a canonical function type that
-    // includes the self parameter as a normal parameter.  It's just
-    // that the error messages you get out of this code are a bit more
-    // inscrutable, particularly for cases where one method has no
-    // self.
-    match (&trait_m.explicit_self, &impl_m.explicit_self) {
-        (&ty::StaticExplicitSelfCategory,
-         &ty::StaticExplicitSelfCategory) => {}
-        (&ty::StaticExplicitSelfCategory, _) => {
-            tcx.sess.span_err(
-                impl_m_span,
-                &format!("method `{}` has a `{}` declaration in the impl, \
-                        but not in the trait",
-                        token::get_name(trait_m.name),
-                        ppaux::explicit_self_category_to_str(
-                            &impl_m.explicit_self))[]);
-            return;
-        }
-        (_, &ty::StaticExplicitSelfCategory) => {
-            tcx.sess.span_err(
-                impl_m_span,
-                &format!("method `{}` has a `{}` declaration in the trait, \
-                        but not in the impl",
-                        token::get_name(trait_m.name),
-                        ppaux::explicit_self_category_to_str(
-                            &trait_m.explicit_self))[]);
-            return;
-        }
-        _ => {
-            // Let the type checker catch other errors below
-        }
-    }
-
-    let num_impl_m_type_params = impl_m.generics.types.len(subst::FnSpace);
-    let num_trait_m_type_params = trait_m.generics.types.len(subst::FnSpace);
-    if num_impl_m_type_params != num_trait_m_type_params {
-        span_err!(tcx.sess, impl_m_span, E0049,
-            "method `{}` has {} type parameter{} \
-             but its trait declaration has {} type parameter{}",
-            token::get_name(trait_m.name),
-            num_impl_m_type_params,
-            if num_impl_m_type_params == 1 {""} else {"s"},
-            num_trait_m_type_params,
-            if num_trait_m_type_params == 1 {""} else {"s"});
-        return;
-    }
+fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
+    let def_id = tcx.hir().local_def_id(id);
+    let def = tcx.adt_def(def_id);
+    def.destructor(tcx); // force the destructor to be evaluated
+    check_representable(tcx, span, def_id);
 
-    if impl_m.fty.sig.0.inputs.len() != trait_m.fty.sig.0.inputs.len() {
-        span_err!(tcx.sess, impl_m_span, E0050,
-            "method `{}` has {} parameter{} \
-             but the declaration in trait `{}` has {}",
-            token::get_name(trait_m.name),
-            impl_m.fty.sig.0.inputs.len(),
-            if impl_m.fty.sig.0.inputs.len() == 1 {""} else {"s"},
-            ty::item_path_str(tcx, trait_m.def_id),
-            trait_m.fty.sig.0.inputs.len());
-        return;
+    if def.repr.simd() {
+        check_simd(tcx, span, def_id);
     }
 
-    // This code is best explained by example. Consider a trait:
-    //
-    //     trait Trait<'t,T> {
-    //          fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
-    //     }
-    //
-    // And an impl:
-    //
-    //     impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
-    //          fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
-    //     }
-    //
-    // We wish to decide if those two method types are compatible.
-    //
-    // We start out with trait_to_impl_substs, that maps the trait
-    // type parameters to impl type parameters. This is taken from the
-    // impl trait reference:
-    //
-    //     trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
-    //
-    // We create a mapping `dummy_substs` that maps from the impl type
-    // parameters to fresh types and regions. For type parameters,
-    // this is the identity transform, but we could as well use any
-    // skolemized types. For regions, we convert from bound to free
-    // regions (Note: but only early-bound regions, i.e., those
-    // declared on the impl or used in type parameter bounds).
-    //
-    //     impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
-    //
-    // Now we can apply skol_substs to the type of the impl method
-    // to yield a new function type in terms of our fresh, skolemized
-    // types:
-    //
-    //     <'b> fn(t: &'i0 U0, m: &'b) -> Foo
-    //
-    // We now want to extract and substitute the type of the *trait*
-    // method and compare it. To do so, we must create a compound
-    // substitution by combining trait_to_impl_substs and
-    // impl_to_skol_substs, and also adding a mapping for the method
-    // type parameters. We extend the mapping to also include
-    // the method parameters.
-    //
-    //     trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 }
-    //
-    // Applying this to the trait method type yields:
-    //
-    //     <'a> fn(t: &'i0 U0, m: &'a) -> Foo
-    //
-    // This type is also the same but the name of the bound region ('a
-    // vs 'b).  However, the normal subtyping rules on fn types handle
-    // this kind of equivalency just fine.
-
-    // Create mapping from impl to skolemized.
-    let impl_param_env = ty::construct_parameter_environment(tcx, &impl_m.generics, impl_m_body_id);
-    let impl_to_skol_substs = &impl_param_env.free_substs;
-
-    // Create mapping from trait to skolemized.
-    let trait_to_skol_substs =
-        trait_to_impl_substs
-        .subst(tcx, impl_to_skol_substs)
-        .with_method(impl_to_skol_substs.types.get_slice(subst::FnSpace).to_vec(),
-                     impl_to_skol_substs.regions().get_slice(subst::FnSpace).to_vec());
-
-    // Check region bounds.
-    if !check_region_bounds_on_impl_method(tcx,
-                                           impl_m_span,
-                                           impl_m,
-                                           &trait_m.generics,
-                                           &impl_m.generics,
-                                           &trait_to_skol_substs,
-                                           impl_to_skol_substs) {
-        return;
-    }
+    check_transparent(tcx, span, def_id);
+    check_packed(tcx, span, def_id);
+}
 
-    // Check bounds. Note that the bounds from the impl may reference
-    // late-bound regions declared on the impl, so liberate those.
-    // This requires two artificial binding scopes -- one for the impl,
-    // and one for the method.
-    //
-    // An example would be:
-    //
-    //     trait Foo<T> { fn method<U:Bound<T>>() { ... } }
-    //
-    //     impl<'a> Foo<&'a T> for &'a U {
-    //         fn method<U:Bound<&'a T>>() { ... }
-    //     }
-    //
-    // Here, the region parameter `'a` is late-bound, so in the bound
-    // `Bound<&'a T>`, the lifetime `'a` will be late-bound with a
-    // depth of 3 (it is nested within 3 binders: the impl, method,
-    // and trait-ref itself). So when we do the liberation, we have
-    // two introduce two `ty::Binder` scopes, one for the impl and one
-    // the method.
-    //
-    // The only late-bounded regions that can possibly appear here are
-    // from the impl, not the method. This is because region
-    // parameters declared on the method which appear in a type bound
-    // would be early bound. On the trait side, there can be no
-    // late-bound lifetimes because trait definitions do not introduce
-    // a late region binder.
-    let trait_bounds =
-        trait_m.generics.types.get_slice(subst::FnSpace).iter()
-        .map(|trait_param_def| &trait_param_def.bounds);
-    let impl_bounds =
-        impl_m.generics.types.get_slice(subst::FnSpace).iter()
-        .map(|impl_param_def| &impl_param_def.bounds);
-    for (i, (trait_param_bounds, impl_param_bounds)) in
-        trait_bounds.zip(impl_bounds).enumerate()
-    {
-        // Check that the impl does not require any builtin-bounds
-        // that the trait does not guarantee:
-        let extra_bounds =
-            impl_param_bounds.builtin_bounds -
-            trait_param_bounds.builtin_bounds;
-        if !extra_bounds.is_empty() {
-            span_err!(tcx.sess, impl_m_span, E0051,
-                "in method `{}`, type parameter {} requires `{}`, \
-                 which is not required by the corresponding type parameter \
-                 in the trait declaration",
-                token::get_name(trait_m.name),
-                i,
-                extra_bounds.user_string(tcx));
-           return;
-        }
-
-        // Check that the trait bounds of the trait imply the bounds of its
-        // implementation.
-        //
-        // FIXME(pcwalton): We could be laxer here regarding sub- and super-
-        // traits, but I doubt that'll be wanted often, so meh.
-        for impl_trait_bound in impl_param_bounds.trait_bounds.iter() {
-            debug!("compare_impl_method(): impl-trait-bound subst");
-            let impl_trait_bound =
-                impl_trait_bound.subst(tcx, impl_to_skol_substs);
-
-            // There may be late-bound regions from the impl in the
-            // impl's bound, so "liberate" those. Note that the
-            // trait_to_skol_substs is derived from the impl's
-            // trait-ref, and the late-bound regions appearing there
-            // have already been liberated, so the result should match
-            // up.
-
-            let found_match_in_trait =
-                trait_param_bounds.trait_bounds.iter().any(|trait_bound| {
-                    debug!("compare_impl_method(): trait-bound subst");
-                    let trait_bound =
-                        trait_bound.subst(tcx, &trait_to_skol_substs);
-                    infer::mk_sub_poly_trait_refs(&infcx,
-                                                  true,
-                                                  infer::Misc(impl_m_span),
-                                                  trait_bound,
-                                                  impl_trait_bound.clone()).is_ok()
-                });
+fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
+    let def_id = tcx.hir().local_def_id(id);
+    let def = tcx.adt_def(def_id);
+    def.destructor(tcx); // force the destructor to be evaluated
+    check_representable(tcx, span, def_id);
+    check_transparent(tcx, span, def_id);
+    check_union_fields(tcx, span, def_id);
+    check_packed(tcx, span, def_id);
+}
 
-            if !found_match_in_trait {
-                span_err!(tcx.sess, impl_m_span, E0052,
-                          "in method `{}`, type parameter {} requires bound `{}`, which is not \
-                           required by the corresponding type parameter in the trait declaration",
-                          token::get_name(trait_m.name),
-                          i,
-                          impl_trait_bound.user_string(tcx));
+/// When the `#![feature(untagged_unions)]` gate is active,
+/// check that the fields of the `union` does not contain fields that need dropping.
+fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool {
+    let item_type = tcx.type_of(item_def_id);
+    if let ty::Adt(def, substs) = item_type.kind {
+        assert!(def.is_union());
+        let fields = &def.non_enum_variant().fields;
+        for field in fields {
+            let field_ty = field.ty(tcx, substs);
+            // We are currently checking the type this field came from, so it must be local.
+            let field_span = tcx.hir().span_if_local(field.did).unwrap();
+            let param_env = tcx.param_env(field.did);
+            if field_ty.needs_drop(tcx, param_env) {
+                struct_span_err!(tcx.sess, field_span, E0740,
+                                    "unions may not contain fields that need dropping")
+                            .span_note(field_span,
+                                        "`std::mem::ManuallyDrop` can be used to wrap the type")
+                            .emit();
+                return false;
             }
         }
+    } else {
+        span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind);
     }
+    return true;
+}
 
-    // We now need to check that the signature of the impl method is
-    // compatible with that of the trait method. We do this by
-    // checking that `impl_fty <: trait_fty`.
-    //
-    // FIXME. Unfortunately, this doesn't quite work right now because
-    // associated type normalization is not integrated into subtype
-    // checks. For the comparison to be valid, we need to
-    // normalize the associated types in the impl/trait methods
-    // first. However, because function types bind regions, just
-    // calling `normalize_associated_types_in` would have no effect on
-    // any associated types appearing in the fn arguments or return
-    // type.
-
-
-    // Compute skolemized form of impl and trait method tys.
-    let impl_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_m.fty.clone()));
-    let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
-    let trait_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(trait_m.fty.clone()));
-    let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
-
-    let err = infcx.try(|snapshot| {
-        let origin = infer::MethodCompatCheck(impl_m_span);
-
-        let (impl_sig, _) =
-            infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
-                                                            infer::HigherRankedType,
-                                                            &impl_m.fty.sig);
-        let impl_sig =
-            impl_sig.subst(tcx, impl_to_skol_substs);
-        let impl_sig =
-            assoc::normalize_associated_types_in(&infcx,
-                                                 &impl_param_env,
-                                                 &mut fulfillment_cx,
-                                                 impl_m_span,
-                                                 impl_m_body_id,
-                                                 &impl_sig);
-        let impl_fty =
-            ty::mk_bare_fn(tcx,
-                           None,
-                           tcx.mk_bare_fn(ty::BareFnTy { unsafety: impl_m.fty.unsafety,
-                                                         abi: impl_m.fty.abi,
-                                                         sig: ty::Binder(impl_sig) }));
-        debug!("compare_impl_method: impl_fty={}",
-               impl_fty.repr(tcx));
-
-        let (trait_sig, skol_map) =
-            infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
-        let trait_sig =
-            trait_sig.subst(tcx, &trait_to_skol_substs);
-        let trait_sig =
-            assoc::normalize_associated_types_in(&infcx,
-                                                 &impl_param_env,
-                                                 &mut fulfillment_cx,
-                                                 impl_m_span,
-                                                 impl_m_body_id,
-                                                 &trait_sig);
-        let trait_fty =
-            ty::mk_bare_fn(tcx,
-                           None,
-                           tcx.mk_bare_fn(ty::BareFnTy { unsafety: trait_m.fty.unsafety,
-                                                         abi: trait_m.fty.abi,
-                                                         sig: ty::Binder(trait_sig) }));
-
-        debug!("compare_impl_method: trait_fty={}",
-               trait_fty.repr(tcx));
-
-        try!(infer::mk_subty(&infcx, false, origin, impl_fty, trait_fty));
-
-        infcx.leak_check(&skol_map, snapshot)
-    });
+/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
+/// projections that would result in "inheriting lifetimes".
+fn check_opaque<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    substs: SubstsRef<'tcx>,
+    span: Span,
+    origin: &hir::OpaqueTyOrigin,
+) {
+    check_opaque_for_inheriting_lifetimes(tcx, def_id, span);
+    check_opaque_for_cycles(tcx, def_id, substs, span, origin);
+}
 
-    match err {
-        Ok(()) => { }
-        Err(terr) => {
-            debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
-                   impl_fty.repr(tcx),
-                   trait_fty.repr(tcx));
-            span_err!(tcx.sess, impl_m_span, E0053,
-                      "method `{}` has an incompatible type for trait: {}",
-                      token::get_name(trait_m.name),
-                      ty::type_err_to_str(tcx, &terr));
-            return;
-        }
-    }
+/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
+/// in "inheriting lifetimes".
+fn check_opaque_for_inheriting_lifetimes(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    span: Span,
+) {
+    let item = tcx.hir().expect_item(
+        tcx.hir().as_local_hir_id(def_id).expect("opaque type is not local"));
+    debug!("check_opaque_for_inheriting_lifetimes: def_id={:?} span={:?} item={:?}",
+           def_id, span, item);
+
+    #[derive(Debug)]
+    struct ProhibitOpaqueVisitor<'tcx> {
+        opaque_identity_ty: Ty<'tcx>,
+        generics: &'tcx ty::Generics,
+    };
 
-    // Run the fulfillment context to completion to accommodate any
-    // associated type normalizations that may have occurred.
-    match fulfillment_cx.select_all_or_error(&infcx, &impl_param_env) {
-        Ok(()) => { }
-        Err(errors) => {
-            traits::report_fulfillment_errors(&infcx, &errors);
+    impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+            debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
+            if t == self.opaque_identity_ty { false } else { t.super_visit_with(self) }
         }
-    }
 
-    // Finally, resolve all regions. This catches wily misuses of lifetime
-    // parameters.
-    infcx.resolve_regions_and_report_errors(impl_m_body_id);
-
-    /// Check that region bounds on impl method are the same as those on the trait. In principle,
-    /// it could be ok for there to be fewer region bounds on the impl method, but this leads to an
-    /// annoying corner case that is painful to handle (described below), so for now we can just
-    /// forbid it.
-    ///
-    /// Example (see `src/test/compile-fail/regions-bound-missing-bound-in-impl.rs`):
-    ///
-    /// ```
-    /// trait Foo<'a> {
-    ///     fn method1<'b>();
-    ///     fn method2<'b:'a>();
-    /// }
-    ///
-    /// impl<'a> Foo<'a> for ... {
-    ///     fn method1<'b:'a>() { .. case 1, definitely bad .. }
-    ///     fn method2<'b>() { .. case 2, could be ok .. }
-    /// }
-    /// ```
-    ///
-    /// The "definitely bad" case is case #1. Here, the impl adds an extra constraint not present
-    /// in the trait.
-    ///
-    /// The "maybe bad" case is case #2. Here, the impl adds an extra constraint not present in the
-    /// trait. We could in principle allow this, but it interacts in a complex way with early/late
-    /// bound resolution of lifetimes. Basically the presence or absence of a lifetime bound
-    /// affects whether the lifetime is early/late bound, and right now the code breaks if the
-    /// trait has an early bound lifetime parameter and the method does not.
-    fn check_region_bounds_on_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                                span: Span,
-                                                impl_m: &ty::Method<'tcx>,
-                                                trait_generics: &ty::Generics<'tcx>,
-                                                impl_generics: &ty::Generics<'tcx>,
-                                                trait_to_skol_substs: &Substs<'tcx>,
-                                                impl_to_skol_substs: &Substs<'tcx>)
-                                                -> bool
-    {
+        fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
+            debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r);
+            if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r {
+                return *index < self.generics.parent_count as u32;
+            }
 
-        let trait_params = trait_generics.regions.get_slice(subst::FnSpace);
-        let impl_params = impl_generics.regions.get_slice(subst::FnSpace);
-
-        debug!("check_region_bounds_on_impl_method: \
-               trait_generics={} \
-               impl_generics={} \
-               trait_to_skol_substs={} \
-               impl_to_skol_substs={}",
-               trait_generics.repr(tcx),
-               impl_generics.repr(tcx),
-               trait_to_skol_substs.repr(tcx),
-               impl_to_skol_substs.repr(tcx));
-
-        // Must have same number of early-bound lifetime parameters.
-        // Unfortunately, if the user screws up the bounds, then this
-        // will change classification between early and late.  E.g.,
-        // if in trait we have `<'a,'b:'a>`, and in impl we just have
-        // `<'a,'b>`, then we have 2 early-bound lifetime parameters
-        // in trait but 0 in the impl. But if we report "expected 2
-        // but found 0" it's confusing, because it looks like there
-        // are zero. Since I don't quite know how to phrase things at
-        // the moment, give a kind of vague error message.
-        if trait_params.len() != impl_params.len() {
-            tcx.sess.span_err(
-                span,
-                &format!("lifetime parameters or bounds on method `{}` do \
-                         not match the trait declaration",
-                        token::get_name(impl_m.name))[]);
-            return false;
+            r.super_visit_with(self)
         }
+    }
 
-        // Each parameter `'a:'b+'c+'d` in trait should have the same
-        // set of bounds in the impl, after subst.
-        for (trait_param, impl_param) in
-            trait_params.iter().zip(
-                impl_params.iter())
-        {
-            let trait_bounds =
-                trait_param.bounds.subst(tcx, trait_to_skol_substs);
-            let impl_bounds =
-                impl_param.bounds.subst(tcx, impl_to_skol_substs);
-
-            debug!("check_region_bounds_on_impl_method: \
-                   trait_param={} \
-                   impl_param={} \
-                   trait_bounds={} \
-                   impl_bounds={}",
-                   trait_param.repr(tcx),
-                   impl_param.repr(tcx),
-                   trait_bounds.repr(tcx),
-                   impl_bounds.repr(tcx));
-
-            // Collect the set of bounds present in trait but not in
-            // impl.
-            let missing: Vec<ty::Region> =
-                trait_bounds.iter()
-                .filter(|&b| !impl_bounds.contains(b))
-                .map(|&b| b)
-                .collect();
-
-            // Collect set present in impl but not in trait.
-            let extra: Vec<ty::Region> =
-                impl_bounds.iter()
-                .filter(|&b| !trait_bounds.contains(b))
-                .map(|&b| b)
-                .collect();
-
-            debug!("missing={} extra={}",
-                   missing.repr(tcx), extra.repr(tcx));
-
-            let err = if missing.len() != 0 || extra.len() != 0 {
-                tcx.sess.span_err(
-                    span,
-                    &format!(
-                        "the lifetime parameter `{}` declared in the impl \
-                         has a distinct set of bounds \
-                         from its counterpart `{}` \
-                         declared in the trait",
-                        impl_param.name.user_string(tcx),
-                        trait_param.name.user_string(tcx))[]);
-                true
-            } else {
-                false
+    let prohibit_opaque = match item.kind {
+        ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::AsyncFn, .. }) |
+        ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn, .. }) => {
+            let mut visitor = ProhibitOpaqueVisitor {
+                opaque_identity_ty: tcx.mk_opaque(
+                    def_id, InternalSubsts::identity_for_item(tcx, def_id)),
+                generics: tcx.generics_of(def_id),
             };
+            debug!("check_opaque_for_inheriting_lifetimes: visitor={:?}", visitor);
 
-            if missing.len() != 0 {
-                tcx.sess.span_note(
-                    span,
-                    &format!("the impl is missing the following bounds: `{}`",
-                            missing.user_string(tcx))[]);
-            }
+            tcx.predicates_of(def_id).predicates.iter().any(
+                |(predicate, _)| predicate.visit_with(&mut visitor))
+        },
+        _ => false,
+    };
 
-            if extra.len() != 0 {
-                tcx.sess.span_note(
-                    span,
-                    &format!("the impl has the following extra bounds: `{}`",
-                            extra.user_string(tcx))[]);
-            }
+    debug!("check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}", prohibit_opaque);
+    if prohibit_opaque {
+        let is_async = match item.kind {
+            ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin {
+                hir::OpaqueTyOrigin::AsyncFn => true,
+                _ => false,
+            },
+            _ => unreachable!(),
+        };
 
-            if err {
-                return false;
+        tcx.sess.span_err(span, &format!(
+            "`{}` return type cannot contain a projection or `Self` that references lifetimes from \
+             a parent scope",
+            if is_async { "async fn" } else { "impl Trait" },
+        ));
+    }
+}
+
+/// Checks that an opaque type does not contain cycles.
+fn check_opaque_for_cycles<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    substs: SubstsRef<'tcx>,
+    span: Span,
+    origin: &hir::OpaqueTyOrigin,
+) {
+    if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id, substs) {
+        if let hir::OpaqueTyOrigin::AsyncFn = origin {
+            struct_span_err!(
+                tcx.sess, span, E0733,
+                "recursion in an `async fn` requires boxing",
+            )
+            .span_label(span, "recursive `async fn`")
+            .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`.")
+            .emit();
+        } else {
+            let mut err = struct_span_err!(
+                tcx.sess, span, E0720,
+                "opaque type expands to a recursive type",
+            );
+            err.span_label(span, "expands to a recursive type");
+            if let ty::Opaque(..) = partially_expanded_type.kind {
+                err.note("type resolves to itself");
+            } else {
+                err.note(&format!("expanded type is `{}`", partially_expanded_type));
             }
+            err.emit();
         }
-
-        return true;
     }
 }
 
-fn check_cast(fcx: &FnCtxt,
-              cast_expr: &ast::Expr,
-              e: &ast::Expr,
-              t: &ast::Ty) {
-    let id = cast_expr.id;
-    let span = cast_expr.span;
-
-    // Find the type of `e`. Supply hints based on the type we are casting to,
-    // if appropriate.
-    let t_1 = fcx.to_ty(t);
-    let t_1 = structurally_resolved_type(fcx, span, t_1);
+// Forbid defining intrinsics in Rust code,
+// as they must always be defined by the compiler.
+fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) {
+    if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
+        tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block");
+    }
+}
 
-    check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1));
+pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item) {
+    debug!(
+        "check_item_type(it.hir_id={}, it.name={})",
+        it.hir_id,
+        tcx.def_path_str(tcx.hir().local_def_id(it.hir_id))
+    );
+    let _indenter = indenter();
+    match it.kind {
+        // Consts can play a role in type-checking, so they are included here.
+        hir::ItemKind::Static(..) => {
+            let def_id = tcx.hir().local_def_id(it.hir_id);
+            tcx.typeck_tables_of(def_id);
+            maybe_check_static_with_link_section(tcx, def_id, it.span);
+        }
+        hir::ItemKind::Const(..) => {
+            tcx.typeck_tables_of(tcx.hir().local_def_id(it.hir_id));
+        }
+        hir::ItemKind::Enum(ref enum_definition, _) => {
+            check_enum(tcx, it.span, &enum_definition.variants, it.hir_id);
+        }
+        hir::ItemKind::Fn(..) => {} // entirely within check_item_body
+        hir::ItemKind::Impl(.., ref impl_item_refs) => {
+            debug!("ItemKind::Impl {} with id {}", it.ident, it.hir_id);
+            let impl_def_id = tcx.hir().local_def_id(it.hir_id);
+            if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) {
+                check_impl_items_against_trait(
+                    tcx,
+                    it.span,
+                    impl_def_id,
+                    impl_trait_ref,
+                    impl_item_refs,
+                );
+                let trait_def_id = impl_trait_ref.def_id;
+                check_on_unimplemented(tcx, trait_def_id, it);
+            }
+        }
+        hir::ItemKind::Trait(_, _, _, _, ref items) => {
+            let def_id = tcx.hir().local_def_id(it.hir_id);
+            check_on_unimplemented(tcx, def_id, it);
 
-    let t_e = fcx.expr_ty(e);
+            for item in items.iter() {
+                let item = tcx.hir().trait_item(item.id);
+                if let hir::TraitItemKind::Method(sig, _) = &item.kind {
+                    let abi = sig.header.abi;
+                    fn_maybe_err(tcx, item.ident.span, abi);
+                }
+            }
+        }
+        hir::ItemKind::Struct(..) => {
+            check_struct(tcx, it.hir_id, it.span);
+        }
+        hir::ItemKind::Union(..) => {
+            check_union(tcx, it.hir_id, it.span);
+        }
+        hir::ItemKind::OpaqueTy(hir::OpaqueTy{origin, ..}) => {
+            let def_id = tcx.hir().local_def_id(it.hir_id);
 
-    debug!("t_1={}", fcx.infcx().ty_to_string(t_1));
-    debug!("t_e={}", fcx.infcx().ty_to_string(t_e));
+            let substs = InternalSubsts::identity_for_item(tcx, def_id);
+            check_opaque(tcx, def_id, substs, it.span, &origin);
+        }
+        hir::ItemKind::TyAlias(..) => {
+            let def_id = tcx.hir().local_def_id(it.hir_id);
+            let pty_ty = tcx.type_of(def_id);
+            let generics = tcx.generics_of(def_id);
+            check_bounds_are_used(tcx, &generics, pty_ty);
+        }
+        hir::ItemKind::ForeignMod(ref m) => {
+            check_abi(tcx, it.span, m.abi);
 
-    if ty::type_is_error(t_e) {
-        fcx.write_error(id);
-        return
-    }
+            if m.abi == Abi::RustIntrinsic {
+                for item in &m.items {
+                    intrinsic::check_intrinsic_type(tcx, item);
+                }
+            } else if m.abi == Abi::PlatformIntrinsic {
+                for item in &m.items {
+                    intrinsic::check_platform_intrinsic_type(tcx, item);
+                }
+            } else {
+                for item in &m.items {
+                    let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id));
+                    let own_counts = generics.own_counts();
+                    if generics.params.len() - own_counts.lifetimes != 0 {
+                        let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) {
+                            (_, 0) => ("type", "types", Some("u32")),
+                            // We don't specify an example value, because we can't generate
+                            // a valid value for any type.
+                            (0, _) => ("const", "consts", None),
+                            _ => ("type or const", "types or consts", None),
+                        };
+                        struct_span_err!(
+                            tcx.sess,
+                            item.span,
+                            E0044,
+                            "foreign items may not have {} parameters",
+                            kinds,
+                        ).span_label(
+                            item.span,
+                            &format!("can't have {} parameters", kinds),
+                        ).help(
+                            // FIXME: once we start storing spans for type arguments, turn this
+                            // into a suggestion.
+                            &format!(
+                                "replace the {} parameters with concrete {}{}",
+                                kinds,
+                                kinds_pl,
+                                egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(),
+                            ),
+                        ).emit();
+                    }
 
-    if !fcx.type_is_known_to_be_sized(t_1, cast_expr.span) {
-        let tstr = fcx.infcx().ty_to_string(t_1);
-        fcx.type_error_message(span, |actual| {
-            format!("cast to unsized type: `{}` as `{}`", actual, tstr)
-        }, t_e, None);
-        match t_e.sty {
-            ty::ty_rptr(_, ty::mt { mutbl: mt, .. }) => {
-                let mtstr = match mt {
-                    ast::MutMutable => "mut ",
-                    ast::MutImmutable => ""
-                };
-                if ty::type_is_trait(t_1) {
-                    span_help!(fcx.tcx().sess, t.span, "did you mean `&{}{}`?", mtstr, tstr);
-                } else {
-                    span_help!(fcx.tcx().sess, span,
-                               "consider using an implicit coercion to `&{}{}` instead",
-                               mtstr, tstr);
+                    if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind {
+                        require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span);
+                    }
                 }
             }
-            ty::ty_uniq(..) => {
-                span_help!(fcx.tcx().sess, t.span, "did you mean `Box<{}>`?", tstr);
-            }
-            _ => {
-                span_help!(fcx.tcx().sess, e.span,
-                           "consider using a box or reference as appropriate");
-            }
         }
-        fcx.write_error(id);
-        return
+        _ => { /* nothing to do */ }
     }
+}
 
-    if ty::type_is_trait(t_1) {
-        // This will be looked up later on.
-        vtable::check_object_cast(fcx, cast_expr, e, t_1);
-        fcx.write_ty(id, t_1);
+fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: DefId, span: Span) {
+    // Only restricted on wasm32 target for now
+    if !tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
         return
     }
 
-    let t_1 = structurally_resolved_type(fcx, span, t_1);
-    let t_e = structurally_resolved_type(fcx, span, t_e);
-
-    if ty::type_is_nil(t_e) {
-        fcx.type_error_message(span, |actual| {
-            format!("cast from nil: `{}` as `{}`",
-                    actual,
-                    fcx.infcx().ty_to_string(t_1))
-        }, t_e, None);
-    } else if ty::type_is_nil(t_1) {
-        fcx.type_error_message(span, |actual| {
-            format!("cast to nil: `{}` as `{}`",
-                    actual,
-                    fcx.infcx().ty_to_string(t_1))
-        }, t_e, None);
-    }
-
-    let t_e_is_bare_fn_item = ty::type_is_bare_fn_item(t_e);
-
-    let t_1_is_scalar = ty::type_is_scalar(t_1);
-    let t_1_is_char = ty::type_is_char(t_1);
-    let t_1_is_bare_fn = ty::type_is_bare_fn(t_1);
-    let t_1_is_float = ty::type_is_floating_point(t_1);
-
-    // casts to scalars other than `char` and `bare fn` are trivial
-    let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn;
-    if t_e_is_bare_fn_item && t_1_is_bare_fn {
-        demand::coerce(fcx, e.span, t_1, &*e);
-    } else if ty::type_is_c_like_enum(fcx.tcx(), t_e) && t_1_is_trivial {
-        if t_1_is_float || ty::type_is_unsafe_ptr(t_1) {
-            fcx.type_error_message(span, |actual| {
-                format!("illegal cast; cast through an \
-                         integer first: `{}` as `{}`",
-                        actual,
-                        fcx.infcx().ty_to_string(t_1))
-            }, t_e, None);
-        }
-        // casts from C-like enums are allowed
-    } else if t_1_is_char {
-        let t_e = fcx.infcx().shallow_resolve(t_e);
-        if t_e.sty != ty::ty_uint(ast::TyU8) {
-            fcx.type_error_message(span, |actual| {
-                format!("only `u8` can be cast as \
-                         `char`, not `{}`", actual)
-            }, t_e, None);
-        }
-    } else if t_1.sty == ty::ty_bool {
-        span_err!(fcx.tcx().sess, span, E0054,
-            "cannot cast as `bool`, compare with zero instead");
-    } else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) {
-        fn types_compatible<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
-                                      t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool {
-            match t1.sty {
-                ty::ty_vec(_, Some(_)) => {}
-                _ => return false
-            }
-            if ty::type_needs_infer(t2) {
-                // This prevents this special case from going off when casting
-                // to a type that isn't fully specified; e.g. `as *_`. (Issue
-                // #14893.)
-                return false
-            }
+    // If `#[link_section]` is missing, then nothing to verify
+    let attrs = tcx.codegen_fn_attrs(id);
+    if attrs.link_section.is_none() {
+        return
+    }
 
-            let el = ty::sequence_element_type(fcx.tcx(), t1);
-            infer::mk_eqty(fcx.infcx(),
-                           false,
-                           infer::Misc(sp),
-                           el,
-                           t2).is_ok()
-        }
-
-        // Due to the limitations of LLVM global constants,
-        // region pointers end up pointing at copies of
-        // vector elements instead of the original values.
-        // To allow unsafe pointers to work correctly, we
-        // need to special-case obtaining an unsafe pointer
-        // from a region pointer to a vector.
-
-        /* this cast is only allowed from &[T, ..n] to *T or
-        &T to *T. */
-        match (&t_e.sty, &t_1.sty) {
-            (&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }),
-             &ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable }))
-            if types_compatible(fcx, e.span, mt1, mt2) => {
-                /* this case is allowed */
-            }
-            _ => {
-                demand::coerce(fcx, e.span, t_1, &*e);
-            }
+    // For the wasm32 target statics with `#[link_section]` are placed into custom
+    // sections of the final output file, but this isn't link custom sections of
+    // other executable formats. Namely we can only embed a list of bytes,
+    // nothing with pointers to anything else or relocations. If any relocation
+    // show up, reject them here.
+    // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
+    // the consumer's responsibility to ensure all bytes that have been read
+    // have defined values.
+    let instance = ty::Instance::mono(tcx, id);
+    let cid = GlobalId {
+        instance,
+        promoted: None
+    };
+    let param_env = ty::ParamEnv::reveal_all();
+    if let Ok(static_) = tcx.const_eval(param_env.and(cid)) {
+        let alloc = if let ty::ConstKind::Value(ConstValue::ByRef { alloc, .. }) = static_.val {
+            alloc
+        } else {
+            bug!("Matching on non-ByRef static")
+        };
+        if alloc.relocations().len() != 0 {
+            let msg = "statics with a custom `#[link_section]` must be a \
+                       simple list of bytes on the wasm target with no \
+                       extra levels of indirection such as references";
+            tcx.sess.span_err(span, msg);
         }
-    } else if !(ty::type_is_scalar(t_e) && t_1_is_trivial) {
-        /*
-        If more type combinations should be supported than are
-        supported here, then file an enhancement issue and
-        record the issue number in this comment.
-        */
-        fcx.type_error_message(span, |actual| {
-            format!("non-scalar cast: `{}` as `{}`",
-                    actual,
-                    fcx.infcx().ty_to_string(t_1))
-        }, t_e, None);
-    } else if ty::type_is_unsafe_ptr(t_e) && t_1_is_float {
-        fcx.type_error_message(span, |actual| {
-            format!("cannot cast from pointer to float directly: `{}` as `{}`; cast through an \
-                     integer first",
-                    actual,
-                    fcx.infcx().ty_to_string(t_1))
-        }, t_e, None);
-    }
-
-    fcx.write_ty(id, t_1);
+    }
 }
 
-impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
-    fn tcx(&self) -> &ty::ctxt<'tcx> { self.ccx.tcx }
+fn check_on_unimplemented(tcx: TyCtxt<'_>, trait_def_id: DefId, item: &hir::Item) {
+    let item_def_id = tcx.hir().local_def_id(item.hir_id);
+    // an error would be reported if this fails.
+    let _ = traits::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id);
+}
 
-    fn get_item_type_scheme(&self, id: ast::DefId) -> ty::TypeScheme<'tcx> {
-        ty::lookup_item_type(self.tcx(), id)
-    }
+fn report_forbidden_specialization(
+    tcx: TyCtxt<'_>,
+    impl_item: &hir::ImplItem,
+    parent_impl: DefId,
+) {
+    let mut err = struct_span_err!(
+        tcx.sess, impl_item.span, E0520,
+        "`{}` specializes an item from a parent `impl`, but \
+         that item is not marked `default`",
+        impl_item.ident);
+    err.span_label(impl_item.span, format!("cannot specialize default item `{}`",
+                                            impl_item.ident));
 
-    fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef<'tcx>> {
-        ty::lookup_trait_def(self.tcx(), id)
+    match tcx.span_of_impl(parent_impl) {
+        Ok(span) => {
+            err.span_label(span, "parent `impl` is here");
+            err.note(&format!("to specialize, `{}` in the parent `impl` must be marked `default`",
+                              impl_item.ident));
+        }
+        Err(cname) => {
+            err.note(&format!("parent implementation is in crate `{}`", cname));
+        }
     }
 
-    fn get_free_substs(&self) -> Option<&Substs<'tcx>> {
-        Some(&self.inh.param_env.free_substs)
-    }
+    err.emit();
+}
 
-    fn ty_infer(&self, _span: Span) -> Ty<'tcx> {
-        self.infcx().next_ty_var()
-    }
+fn check_specialization_validity<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_def: &ty::TraitDef,
+    trait_item: &ty::AssocItem,
+    impl_id: DefId,
+    impl_item: &hir::ImplItem,
+) {
+    let kind = match impl_item.kind {
+        hir::ImplItemKind::Const(..) => ty::AssocKind::Const,
+        hir::ImplItemKind::Method(..) => ty::AssocKind::Method,
+        hir::ImplItemKind::OpaqueTy(..) => ty::AssocKind::OpaqueTy,
+        hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type,
+    };
 
-    fn projected_ty_from_poly_trait_ref(&self,
-                                        span: Span,
-                                        poly_trait_ref: ty::PolyTraitRef<'tcx>,
-                                        item_name: ast::Name)
-                                        -> Ty<'tcx>
-    {
-        let (trait_ref, _) =
-            self.infcx().replace_late_bound_regions_with_fresh_var(
-                span,
-                infer::LateBoundRegionConversionTime::AssocTypeProjection(item_name),
-                &poly_trait_ref);
+    let mut ancestor_impls = trait_def.ancestors(tcx, impl_id)
+        .skip(1)
+        .filter_map(|parent| {
+            if parent.is_from_trait() {
+                None
+            } else {
+                Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id)))
+            }
+        })
+        .peekable();
 
-        self.normalize_associated_type(span, trait_ref, item_name)
+    if ancestor_impls.peek().is_none() {
+        // No parent, nothing to specialize.
+        return;
     }
 
-    fn projected_ty(&self,
-                    span: Span,
-                    trait_ref: Rc<ty::TraitRef<'tcx>>,
-                    item_name: ast::Name)
-                    -> Ty<'tcx>
-    {
-        self.normalize_associated_type(span, trait_ref, item_name)
-    }
-}
+    let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| {
+        match parent_item {
+            // Parent impl exists, and contains the parent item we're trying to specialize, but
+            // doesn't mark it `default`.
+            Some(parent_item) if tcx.impl_item_is_final(&parent_item) => {
+                Some(Err(parent_impl.def_id()))
+            }
 
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    fn tcx(&self) -> &ty::ctxt<'tcx> { self.ccx.tcx }
+            // Parent impl contains item and makes it specializable.
+            Some(_) => {
+                Some(Ok(()))
+            }
 
-    pub fn infcx(&self) -> &infer::InferCtxt<'a,'tcx> {
-        &self.inh.infcx
-    }
+            // Parent impl doesn't mention the item. This means it's inherited from the
+            // grandparent. In that case, if parent is a `default impl`, inherited items use the
+            // "defaultness" from the grandparent, else they are final.
+            None => if tcx.impl_is_default(parent_impl.def_id()) {
+                None
+            } else {
+                Some(Err(parent_impl.def_id()))
+            }
+        }
+    });
 
-    pub fn param_env(&self) -> &ty::ParameterEnvironment<'a,'tcx> {
-        &self.inh.param_env
-    }
+    // If `opt_result` is `None`, we have only encoutered `default impl`s that don't contain the
+    // item. This is allowed, the item isn't actually getting specialized here.
+    let result = opt_result.unwrap_or(Ok(()));
 
-    pub fn sess(&self) -> &Session {
-        &self.tcx().sess
+    if let Err(parent_impl) = result {
+        report_forbidden_specialization(tcx, impl_item, parent_impl);
     }
+}
 
-    pub fn err_count_since_creation(&self) -> uint {
-        self.ccx.tcx.sess.err_count() - self.err_count_on_creation
-    }
+fn check_impl_items_against_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    full_impl_span: Span,
+    impl_id: DefId,
+    impl_trait_ref: ty::TraitRef<'tcx>,
+    impl_item_refs: &[hir::ImplItemRef],
+) {
+    let impl_span = tcx.sess.source_map().def_span(full_impl_span);
 
-    /// Resolves all type variables in `t` and then, if any were left
-    /// unresolved, substitutes an error type. This is used after the
-    /// main checking when doing a second pass before writeback. The
-    /// justification is that writeback will produce an error for
-    /// these unconstrained type variables.
-    fn resolve_type_vars_or_error(&self, t: &Ty<'tcx>) -> mc::McResult<Ty<'tcx>> {
-        let t = self.infcx().resolve_type_vars_if_possible(t);
-        if ty::type_has_ty_infer(t) || ty::type_is_error(t) { Err(()) } else { Ok(t) }
-    }
+    // If the trait reference itself is erroneous (so the compilation is going
+    // to fail), skip checking the items here -- the `impl_item` table in `tcx`
+    // isn't populated for such impls.
+    if impl_trait_ref.references_error() { return; }
 
-    pub fn tag(&self) -> String {
-        format!("{:?}", self as *const FnCtxt)
-    }
+    // Locate trait definition and items
+    let trait_def = tcx.trait_def(impl_trait_ref.def_id);
+    let mut overridden_associated_type = None;
 
-    pub fn local_ty(&self, span: Span, nid: ast::NodeId) -> Ty<'tcx> {
-        match self.inh.locals.borrow().get(&nid) {
-            Some(&t) => t,
-            None => {
-                self.tcx().sess.span_bug(
-                    span,
-                    &format!("no type for local variable {}",
-                            nid)[]);
+    let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id));
+
+    // Check existing impl methods to see if they are both present in trait
+    // and compatible with trait signature
+    for impl_item in impl_items() {
+        let ty_impl_item = tcx.associated_item(
+            tcx.hir().local_def_id(impl_item.hir_id));
+        let ty_trait_item = tcx.associated_items(impl_trait_ref.def_id)
+            .find(|ac| Namespace::from(&impl_item.kind) == Namespace::from(ac.kind) &&
+                       tcx.hygienic_eq(ty_impl_item.ident, ac.ident, impl_trait_ref.def_id))
+            .or_else(|| {
+                // Not compatible, but needed for the error message
+                tcx.associated_items(impl_trait_ref.def_id)
+                   .find(|ac| tcx.hygienic_eq(ty_impl_item.ident, ac.ident, impl_trait_ref.def_id))
+            });
+
+        // Check that impl definition matches trait definition
+        if let Some(ty_trait_item) = ty_trait_item {
+            match impl_item.kind {
+                hir::ImplItemKind::Const(..) => {
+                    // Find associated const definition.
+                    if ty_trait_item.kind == ty::AssocKind::Const {
+                        compare_const_impl(tcx,
+                                           &ty_impl_item,
+                                           impl_item.span,
+                                           &ty_trait_item,
+                                           impl_trait_ref);
+                    } else {
+                         let mut err = struct_span_err!(tcx.sess, impl_item.span, E0323,
+                             "item `{}` is an associated const, \
+                              which doesn't match its trait `{}`",
+                             ty_impl_item.ident,
+                             impl_trait_ref.print_only_trait_path());
+                         err.span_label(impl_item.span, "does not match trait");
+                         // We can only get the spans from local trait definition
+                         // Same for E0324 and E0325
+                         if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) {
+                            err.span_label(trait_span, "item in trait");
+                         }
+                         err.emit()
+                    }
+                }
+                hir::ImplItemKind::Method(..) => {
+                    let trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
+                    if ty_trait_item.kind == ty::AssocKind::Method {
+                        compare_impl_method(tcx,
+                                            &ty_impl_item,
+                                            impl_item.span,
+                                            &ty_trait_item,
+                                            impl_trait_ref,
+                                            trait_span);
+                    } else {
+                        let mut err = struct_span_err!(tcx.sess, impl_item.span, E0324,
+                            "item `{}` is an associated method, \
+                             which doesn't match its trait `{}`",
+                            ty_impl_item.ident,
+                            impl_trait_ref.print_only_trait_path());
+                         err.span_label(impl_item.span, "does not match trait");
+                         if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) {
+                            err.span_label(trait_span, "item in trait");
+                         }
+                         err.emit()
+                    }
+                }
+                hir::ImplItemKind::OpaqueTy(..) |
+                hir::ImplItemKind::TyAlias(_) => {
+                    if ty_trait_item.kind == ty::AssocKind::Type {
+                        if ty_trait_item.defaultness.has_value() {
+                            overridden_associated_type = Some(impl_item);
+                        }
+                    } else {
+                        let mut err = struct_span_err!(tcx.sess, impl_item.span, E0325,
+                            "item `{}` is an associated type, \
+                             which doesn't match its trait `{}`",
+                            ty_impl_item.ident,
+                            impl_trait_ref.print_only_trait_path());
+                         err.span_label(impl_item.span, "does not match trait");
+                         if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) {
+                            err.span_label(trait_span, "item in trait");
+                         }
+                         err.emit()
+                    }
+                }
             }
+
+            check_specialization_validity(tcx, trait_def, &ty_trait_item, impl_id, impl_item);
         }
     }
 
-    /// Apply "fallbacks" to some types
-    /// ! gets replaced with (), unconstrained ints with i32, and unconstrained floats with f64.
-    pub fn default_type_parameters(&self) {
-        use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
-        for (_, &mut ref ty) in self.inh.node_types.borrow_mut().iter_mut() {
-            let resolved = self.infcx().resolve_type_vars_if_possible(ty);
-            if self.infcx().type_var_diverges(resolved) {
-                demand::eqtype(self, codemap::DUMMY_SP, *ty, ty::mk_nil(self.tcx()));
-            } else {
-                match self.infcx().type_is_unconstrained_numeric(resolved) {
-                    UnconstrainedInt => {
-                        demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
-                    },
-                    UnconstrainedFloat => {
-                        demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
-                    }
-                    Neither => { }
-                }
+    // Check for missing items from trait
+    let mut missing_items = Vec::new();
+    let mut invalidated_items = Vec::new();
+    let associated_type_overridden = overridden_associated_type.is_some();
+    for trait_item in tcx.associated_items(impl_trait_ref.def_id) {
+        let is_implemented = trait_def.ancestors(tcx, impl_id)
+            .leaf_def(tcx, trait_item.ident, trait_item.kind)
+            .map(|node_item| !node_item.node.is_from_trait())
+            .unwrap_or(false);
+
+        if !is_implemented && !tcx.impl_is_default(impl_id) {
+            if !trait_item.defaultness.has_value() {
+                missing_items.push(trait_item);
+            } else if associated_type_overridden {
+                invalidated_items.push(trait_item.ident);
             }
         }
     }
 
-    #[inline]
-    pub fn write_ty(&self, node_id: ast::NodeId, ty: Ty<'tcx>) {
-        debug!("write_ty({}, {}) in fcx {}",
-               node_id, ppaux::ty_to_string(self.tcx(), ty), self.tag());
-        self.inh.node_types.borrow_mut().insert(node_id, ty);
+    if !missing_items.is_empty() {
+        missing_items_err(tcx, impl_span, &missing_items, full_impl_span);
     }
 
-    pub fn write_object_cast(&self,
-                             key: ast::NodeId,
-                             trait_ref: ty::PolyTraitRef<'tcx>) {
-        debug!("write_object_cast key={} trait_ref={}",
-               key, trait_ref.repr(self.tcx()));
-        self.inh.object_cast_map.borrow_mut().insert(key, trait_ref);
+    if !invalidated_items.is_empty() {
+        let invalidator = overridden_associated_type.unwrap();
+        span_err!(
+            tcx.sess,
+            invalidator.span,
+            E0399,
+            "the following trait items need to be reimplemented as `{}` was overridden: `{}`",
+            invalidator.ident,
+            invalidated_items.iter()
+                .map(|name| name.to_string())
+                .collect::<Vec<_>>().join("`, `")
+        )
     }
+}
 
-    pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts<'tcx>) {
-        if !substs.substs.is_noop() {
-            debug!("write_substs({}, {}) in fcx {}",
-                   node_id,
-                   substs.repr(self.tcx()),
-                   self.tag());
-
-            self.inh.item_substs.borrow_mut().insert(node_id, substs);
+fn missing_items_err(
+    tcx: TyCtxt<'_>,
+    impl_span: Span,
+    missing_items: &[ty::AssocItem],
+    full_impl_span: Span,
+) {
+    let missing_items_msg = missing_items.iter()
+        .map(|trait_item| trait_item.ident.to_string())
+        .collect::<Vec<_>>().join("`, `");
+
+    let mut err = struct_span_err!(
+        tcx.sess,
+        impl_span,
+        E0046,
+        "not all trait items implemented, missing: `{}`",
+        missing_items_msg
+    );
+    err.span_label(impl_span, format!("missing `{}` in implementation", missing_items_msg));
+
+    // `Span` before impl block closing brace.
+    let hi = full_impl_span.hi() - BytePos(1);
+    // Point at the place right before the closing brace of the relevant `impl` to suggest
+    // adding the associated item at the end of its body.
+    let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
+    // Obtain the level of indentation ending in `sugg_sp`.
+    let indentation = tcx.sess.source_map().span_to_margin(sugg_sp).unwrap_or(0);
+    // Make the whitespace that will make the suggestion have the right indentation.
+    let padding: String = (0..indentation).map(|_| " ").collect();
+
+    for trait_item in missing_items {
+        let snippet = suggestion_signature(&trait_item, tcx);
+        let code = format!("{}{}\n{}", padding, snippet, padding);
+        let msg = format!("implement the missing item: `{}`", snippet);
+        let appl = Applicability::HasPlaceholders;
+        if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
+            err.span_label(span, format!("`{}` from trait", trait_item.ident));
+            err.tool_only_span_suggestion(sugg_sp, &msg, code, appl);
+        } else {
+            err.span_suggestion_hidden(sugg_sp, &msg, code, appl);
         }
     }
+    err.emit();
+}
 
-    pub fn write_autoderef_adjustment(&self,
-                                      node_id: ast::NodeId,
-                                      span: Span,
-                                      derefs: uint) {
-        if derefs == 0 { return; }
-        self.write_adjustment(
-            node_id,
-            span,
-            ty::AdjustDerefRef(ty::AutoDerefRef {
-                autoderefs: derefs,
-                autoref: None })
-        );
-    }
+/// Return placeholder code for the given function.
+fn fn_sig_suggestion(sig: &ty::FnSig<'_>, ident: Ident) -> String {
+    let args = sig.inputs()
+        .iter()
+        .map(|ty| Some(match ty.kind {
+            ty::Param(param) if param.name == kw::SelfUpper => "self".to_string(),
+            ty::Ref(reg, ref_ty, mutability) => {
+                let reg = match &format!("{}", reg)[..] {
+                    "'_" | "" => String::new(),
+                    reg => format!("{} ", reg),
+                };
+                match ref_ty.kind {
+                    ty::Param(param) if param.name == kw::SelfUpper => {
+                        format!("&{}{}self", reg, mutability.prefix_str())
+                    }
+                    _ => format!("_: {:?}", ty),
+                }
+            }
+            _ => format!("_: {:?}", ty),
+        }))
+        .chain(std::iter::once(if sig.c_variadic {
+            Some("...".to_string())
+        } else {
+            None
+        }))
+        .filter_map(|arg| arg)
+        .collect::<Vec<String>>()
+        .join(", ");
+    let output = sig.output();
+    let output = if !output.is_unit() {
+        format!(" -> {:?}", output)
+    } else {
+        String::new()
+    };
 
-    pub fn write_adjustment(&self,
-                            node_id: ast::NodeId,
-                            span: Span,
-                            adj: ty::AutoAdjustment<'tcx>) {
-        debug!("write_adjustment(node_id={}, adj={})", node_id, adj.repr(self.tcx()));
+    let unsafety = sig.unsafety.prefix_str();
+    // FIXME: this is not entirely correct, as the lifetimes from borrowed params will
+    // not be present in the `fn` definition, not will we account for renamed
+    // lifetimes between the `impl` and the `trait`, but this should be good enough to
+    // fill in a significant portion of the missing code, and other subsequent
+    // suggestions can help the user fix the code.
+    format!("{}fn {}({}){} {{ unimplemented!() }}", unsafety, ident, args, output)
+}
 
-        if adj.is_identity() {
-            return;
+/// Return placeholder code for the given associated item.
+/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
+/// structured suggestion.
+fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
+    match assoc.kind {
+        ty::AssocKind::Method => {
+            // We skip the binder here because the binder would deanonymize all
+            // late-bound regions, and we don't want method signatures to show up
+            // `as for<'r> fn(&'r MyType)`.  Pretty-printing handles late-bound
+            // regions just fine, showing `fn(&MyType)`.
+            fn_sig_suggestion(tcx.fn_sig(assoc.def_id).skip_binder(), assoc.ident)
+        }
+        ty::AssocKind::Type => format!("type {} = Type;", assoc.ident),
+        // FIXME(type_alias_impl_trait): we should print bounds here too.
+        ty::AssocKind::OpaqueTy => format!("type {} = Type;", assoc.ident),
+        ty::AssocKind::Const => {
+            let ty = tcx.type_of(assoc.def_id);
+            let val = expr::ty_kind_suggestion(ty).unwrap_or("value");
+            format!("const {}: {:?} = {};", assoc.ident, ty, val)
         }
-
-        // Careful: adjustments can imply trait obligations if we are
-        // casting from a concrete type to an object type. I think
-        // it'd probably be nicer to move the logic that creates the
-        // obligation into the code that creates the adjustment, but
-        // that's a bit awkward, so instead we go digging and pull the
-        // obligation out here.
-        self.register_adjustment_obligations(span, &adj);
-        self.inh.adjustments.borrow_mut().insert(node_id, adj);
     }
+}
 
-    /// Basically whenever we are converting from a type scheme into
-    /// the fn body space, we always want to normalize associated
-    /// types as well. This function combines the two.
-    fn instantiate_type_scheme<T>(&self,
-                                  span: Span,
-                                  substs: &Substs<'tcx>,
-                                  value: &T)
-                                  -> T
-        where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx>
-    {
-        let value = value.subst(self.tcx(), substs);
-        let result = self.normalize_associated_types_in(span, &value);
-        debug!("instantiate_type_scheme(value={}, substs={}) = {}",
-               value.repr(self.tcx()),
-               substs.repr(self.tcx()),
-               result.repr(self.tcx()));
-        result
-    }
+/// Checks whether a type can be represented in memory. In particular, it
+/// identifies types that contain themselves without indirection through a
+/// pointer, which would mean their size is unbounded.
+fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: DefId) -> bool {
+    let rty = tcx.type_of(item_def_id);
 
-    /// As `instantiate_type_scheme`, but for the bounds found in a
-    /// generic type scheme.
-    fn instantiate_bounds(&self,
-                          span: Span,
-                          substs: &Substs<'tcx>,
-                          generics: &ty::Generics<'tcx>)
-                          -> ty::GenericBounds<'tcx>
-    {
-        ty::GenericBounds {
-            predicates: self.instantiate_type_scheme(span, substs, &generics.predicates)
+    // Check that it is possible to represent this type. This call identifies
+    // (1) types that contain themselves and (2) types that contain a different
+    // recursive type. It is only necessary to throw an error on those that
+    // contain themselves. For case 2, there must be an inner type that will be
+    // caught by case 1.
+    match rty.is_representable(tcx, sp) {
+        Representability::SelfRecursive(spans) => {
+            let mut err = tcx.recursive_type_with_infinite_size_error(item_def_id);
+            for span in spans {
+                err.span_label(span, "recursive without indirection");
+            }
+            err.emit();
+            return false
         }
+        Representability::Representable | Representability::ContainsRecursive => (),
     }
+    return true;
+}
 
-
-    fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
-        where T : TypeFoldable<'tcx> + Clone + HasProjectionTypes + Repr<'tcx>
-    {
-        self.inh.normalize_associated_types_in(self, span, self.body_id, value)
-    }
-
-    fn normalize_associated_type(&self,
-                                 span: Span,
-                                 trait_ref: Rc<ty::TraitRef<'tcx>>,
-                                 item_name: ast::Name)
-                                 -> Ty<'tcx>
-    {
-        let cause = traits::ObligationCause::new(span,
-                                                 self.body_id,
-                                                 traits::ObligationCauseCode::MiscObligation);
-        self.inh.fulfillment_cx
-            .borrow_mut()
-            .normalize_projection_type(self.infcx(),
-                                       self,
-                                       ty::ProjectionTy {
-                                           trait_ref: trait_ref,
-                                           item_name: item_name,
-                                       },
-                                       cause)
-    }
-
-    fn register_adjustment_obligations(&self,
-                                       span: Span,
-                                       adj: &ty::AutoAdjustment<'tcx>) {
-        match *adj {
-            ty::AdjustReifyFnPointer(..) => {
+pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) {
+    let t = tcx.type_of(def_id);
+    if let ty::Adt(def, substs) = t.kind {
+        if def.is_struct() {
+            let fields = &def.non_enum_variant().fields;
+            if fields.is_empty() {
+                span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty");
+                return;
             }
-            ty::AdjustDerefRef(ref d_r) => {
-                match d_r.autoref {
-                    Some(ref a_r) => {
-                        self.register_autoref_obligations(span, a_r);
-                    }
-                    None => {}
+            let e = fields[0].ty(tcx, substs);
+            if !fields.iter().all(|f| f.ty(tcx, substs) == e) {
+                struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous")
+                                .span_label(sp, "SIMD elements must have the same type")
+                                .emit();
+                return;
+            }
+            match e.kind {
+                ty::Param(_) => { /* struct<T>(T, T, T, T) is ok */ }
+                _ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ }
+                _ => {
+                    span_err!(tcx.sess, sp, E0077,
+                              "SIMD vector element type should be machine type");
+                    return;
                 }
             }
         }
     }
+}
 
-    fn register_autoref_obligations(&self,
-                                    span: Span,
-                                    autoref: &ty::AutoRef<'tcx>) {
-        match *autoref {
-            ty::AutoUnsize(ref unsize) => {
-                self.register_unsize_obligations(span, unsize);
-            }
-            ty::AutoPtr(_, _, None) |
-            ty::AutoUnsafe(_, None) => {
-            }
-            ty::AutoPtr(_, _, Some(ref a_r)) |
-            ty::AutoUnsafe(_, Some(ref a_r)) => {
-                self.register_autoref_obligations(span, &**a_r)
-            }
-            ty::AutoUnsizeUniq(ref unsize) => {
-                self.register_unsize_obligations(span, unsize);
+fn check_packed(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) {
+    let repr = tcx.adt_def(def_id).repr;
+    if repr.packed() {
+        for attr in tcx.get_attrs(def_id).iter() {
+            for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) {
+                if let attr::ReprPacked(pack) = r {
+                    if let Some(repr_pack) = repr.pack {
+                        if pack as u64 != repr_pack.bytes() {
+                            struct_span_err!(
+                                tcx.sess, sp, E0634,
+                                "type has conflicting packed representation hints"
+                            ).emit();
+                        }
+                    }
+                }
             }
         }
+        if repr.align.is_some() {
+            struct_span_err!(tcx.sess, sp, E0587,
+                             "type has conflicting packed and align representation hints").emit();
+        }
+        else if check_packed_inner(tcx, def_id, &mut Vec::new()) {
+            struct_span_err!(tcx.sess, sp, E0588,
+                "packed type cannot transitively contain a `[repr(align)]` type").emit();
+        }
     }
+}
 
-    fn register_unsize_obligations(&self,
-                                   span: Span,
-                                   unsize: &ty::UnsizeKind<'tcx>) {
-        debug!("register_unsize_obligations: unsize={:?}", unsize);
-
-        match *unsize {
-            ty::UnsizeLength(..) => {}
-            ty::UnsizeStruct(ref u, _) => {
-                self.register_unsize_obligations(span, &**u)
+fn check_packed_inner(tcx: TyCtxt<'_>, def_id: DefId, stack: &mut Vec<DefId>) -> bool {
+    let t = tcx.type_of(def_id);
+    if stack.contains(&def_id) {
+        debug!("check_packed_inner: {:?} is recursive", t);
+        return false;
+    }
+    if let ty::Adt(def, substs) = t.kind {
+        if def.is_struct() || def.is_union() {
+            if tcx.adt_def(def.did).repr.align.is_some() {
+                return true;
             }
-            ty::UnsizeVtable(ref ty_trait, self_ty) => {
-                vtable::check_object_safety(self.tcx(), ty_trait, span);
-
-                // If the type is `Foo+'a`, ensures that the type
-                // being cast to `Foo+'a` implements `Foo`:
-                vtable::register_object_cast_obligations(self,
-                                                         span,
-                                                         ty_trait,
-                                                         self_ty);
-
-                // If the type is `Foo+'a`, ensures that the type
-                // being cast to `Foo+'a` outlives `'a`:
-                let cause = traits::ObligationCause { span: span,
-                                                      body_id: self.body_id,
-                                                      code: traits::ObjectCastObligation(self_ty) };
-                self.register_region_obligation(self_ty, ty_trait.bounds.region_bound, cause);
+            // push struct def_id before checking fields
+            stack.push(def_id);
+            for field in &def.non_enum_variant().fields {
+                let f = field.ty(tcx, substs);
+                if let ty::Adt(def, _) = f.kind {
+                    if check_packed_inner(tcx, def.did, stack) {
+                        return true;
+                    }
+                }
             }
+            // only need to pop if not early out
+            stack.pop();
         }
     }
+    false
+}
 
-    /// Returns the type of `def_id` with all generics replaced by by fresh type/region variables.
-    /// Also returns the substitution from the type parameters on `def_id` to the fresh variables.
-    /// Registers any trait obligations specified on `def_id` at the same time.
-    ///
-    /// Note that function is only intended to be used with types (notably, not fns). This is
-    /// because it doesn't do any instantiation of late-bound regions.
-    pub fn instantiate_type(&self,
-                            span: Span,
-                            def_id: ast::DefId)
-                            -> TypeAndSubsts<'tcx>
-    {
-        let type_scheme =
-            ty::lookup_item_type(self.tcx(), def_id);
-        let substs =
-            self.infcx().fresh_substs_for_generics(
-                span,
-                &type_scheme.generics);
-        let bounds =
-            self.instantiate_bounds(span, &substs, &type_scheme.generics);
-        self.add_obligations_for_parameters(
-            traits::ObligationCause::new(
-                span,
-                self.body_id,
-                traits::ItemObligation(def_id)),
-            &bounds);
-        let monotype =
-            self.instantiate_type_scheme(span, &substs, &type_scheme.ty);
-
-        TypeAndSubsts {
-            ty: monotype,
-            substs: substs
-        }
-    }
+/// Emit an error when encountering more or less than one variant in a transparent enum.
+fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: &'tcx ty::AdtDef, sp: Span, did: DefId) {
+    let variant_spans: Vec<_> = adt.variants.iter().map(|variant| {
+        tcx.hir().span_if_local(variant.def_id).unwrap()
+    }).collect();
+    let msg = format!(
+        "needs exactly one variant, but has {}",
+        adt.variants.len(),
+    );
+    let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {}", msg);
+    err.span_label(sp, &msg);
+    if let [start @ .., end] = &*variant_spans {
+        for variant_span in start {
+            err.span_label(*variant_span, "");
+        }
+        err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did)));
+    }
+    err.emit();
+}
 
-    /// Returns the type that this AST path refers to. If the path has no type
-    /// parameters and the corresponding type has type parameters, fresh type
-    /// and/or region variables are substituted.
-    ///
-    /// This is used when checking the constructor in struct literals.
-    fn instantiate_struct_literal_ty(&self,
-                                     did: ast::DefId,
-                                     path: &ast::Path)
-                                     -> TypeAndSubsts<'tcx>
-    {
-        let tcx = self.tcx();
+/// Emit an error when encountering more or less than one non-zero-sized field in a transparent
+/// enum.
+fn bad_non_zero_sized_fields<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    adt: &'tcx ty::AdtDef,
+    field_count: usize,
+    field_spans: impl Iterator<Item = Span>,
+    sp: Span,
+) {
+    let msg = format!("needs exactly one non-zero-sized field, but has {}", field_count);
+    let mut err = struct_span_err!(
+        tcx.sess,
+        sp,
+        E0690,
+        "{}transparent {} {}",
+        if adt.is_enum() { "the variant of a " } else { "" },
+        adt.descr(),
+        msg,
+    );
+    err.span_label(sp, &msg);
+    for sp in field_spans {
+        err.span_label(sp, "this field is non-zero-sized");
+    }
+    err.emit();
+}
 
-        let ty::TypeScheme { generics, ty: decl_ty } = ty::lookup_item_type(tcx, did);
+fn check_transparent(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) {
+    let adt = tcx.adt_def(def_id);
+    if !adt.repr.transparent() {
+        return;
+    }
+    let sp = tcx.sess.source_map().def_span(sp);
 
-        let wants_params =
-            generics.has_type_params(TypeSpace) || generics.has_region_params(TypeSpace);
+    if adt.is_enum() && !tcx.features().transparent_enums {
+        feature_err(
+            &tcx.sess.parse_sess,
+            sym::transparent_enums,
+            sp,
+            "transparent enums are unstable",
+        )
+        .emit();
+    }
 
-        let needs_defaults =
-            wants_params &&
-            path.segments.iter().all(|s| s.parameters.is_empty());
+    if adt.is_union() && !tcx.features().transparent_unions {
+        feature_err(
+            &tcx.sess.parse_sess,
+            sym::transparent_unions,
+            sp,
+            "transparent unions are unstable",
+        )
+        .emit();
+    }
 
-        let substs = if needs_defaults {
-            let tps =
-                self.infcx().next_ty_vars(generics.types.len(TypeSpace));
-            let rps =
-                self.infcx().region_vars_for_defs(path.span,
-                                                  generics.regions.get_slice(TypeSpace));
-            Substs::new_type(tps, rps)
-        } else {
-            astconv::ast_path_substs_for_ty(self, self, &generics, path)
-        };
+    if adt.variants.len() != 1 {
+        bad_variant_count(tcx, adt, sp, def_id);
+        if adt.variants.is_empty() {
+            // Don't bother checking the fields. No variants (and thus no fields) exist.
+            return;
+        }
+    }
 
-        let ty = self.instantiate_type_scheme(path.span, &substs, &decl_ty);
+    // For each field, figure out if it's known to be a ZST and align(1)
+    let field_infos = adt.all_fields().map(|field| {
+        let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
+        let param_env = tcx.param_env(field.did);
+        let layout = tcx.layout_of(param_env.and(ty));
+        // We are currently checking the type this field came from, so it must be local
+        let span = tcx.hir().span_if_local(field.did).unwrap();
+        let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false);
+        let align1 = layout.map(|layout| layout.align.abi.bytes() == 1).unwrap_or(false);
+        (span, zst, align1)
+    });
 
-        TypeAndSubsts { substs: substs, ty: ty }
+    let non_zst_fields = field_infos.clone().filter_map(|(span, zst, _align1)| if !zst {
+        Some(span)
+    } else {
+        None
+    });
+    let non_zst_count = non_zst_fields.clone().count();
+    if non_zst_count != 1 {
+        bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp);
+    }
+    for (span, zst, align1) in field_infos {
+        if zst && !align1 {
+            struct_span_err!(
+                tcx.sess,
+                span,
+                E0691,
+                "zero-sized field in transparent {} has alignment larger than 1",
+                adt.descr(),
+            ).span_label(span, "has alignment larger than 1").emit();
+        }
     }
+}
 
-    pub fn write_nil(&self, node_id: ast::NodeId) {
-        self.write_ty(node_id, ty::mk_nil(self.tcx()));
-    }
-    pub fn write_error(&self, node_id: ast::NodeId) {
-        self.write_ty(node_id, self.tcx().types.err);
-    }
+#[allow(trivial_numeric_casts)]
+pub fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, vs: &'tcx [hir::Variant], id: hir::HirId) {
+    let def_id = tcx.hir().local_def_id(id);
+    let def = tcx.adt_def(def_id);
+    def.destructor(tcx); // force the destructor to be evaluated
 
-    pub fn require_type_meets(&self,
-                              ty: Ty<'tcx>,
-                              span: Span,
-                              code: traits::ObligationCauseCode<'tcx>,
-                              bound: ty::BuiltinBound)
-    {
-        self.register_builtin_bound(
-            ty,
-            bound,
-            traits::ObligationCause::new(span, self.body_id, code));
+    if vs.is_empty() {
+        let attributes = tcx.get_attrs(def_id);
+        if let Some(attr) = attr::find_by_name(&attributes, sym::repr) {
+            struct_span_err!(
+                tcx.sess, attr.span, E0084,
+                "unsupported representation for zero-variant enum")
+                .span_label(sp, "zero-variant enum")
+                .emit();
+        }
     }
 
-    pub fn require_type_is_sized(&self,
-                                 ty: Ty<'tcx>,
-                                 span: Span,
-                                 code: traits::ObligationCauseCode<'tcx>)
-    {
-        self.require_type_meets(ty, span, code, ty::BoundSized);
+    let repr_type_ty = def.repr.discr_type().to_ty(tcx);
+    if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 {
+        if !tcx.features().repr128 {
+            feature_err(
+                &tcx.sess.parse_sess,
+                sym::repr128,
+                sp,
+                "repr with 128-bit type is unstable",
+            )
+            .emit();
+        }
     }
 
-    pub fn require_expr_have_sized_type(&self,
-                                        expr: &ast::Expr,
-                                        code: traits::ObligationCauseCode<'tcx>)
-    {
-        self.require_type_is_sized(self.expr_ty(expr), expr.span, code);
+    for v in vs {
+        if let Some(ref e) = v.disr_expr {
+            tcx.typeck_tables_of(tcx.hir().local_def_id(e.hir_id));
+        }
     }
 
-    pub fn type_is_known_to_be_sized(&self,
-                                     ty: Ty<'tcx>,
-                                     span: Span)
-                                     -> bool
-    {
-        traits::type_known_to_meet_builtin_bound(self.infcx(),
-                                                 self.param_env(),
-                                                 ty,
-                                                 ty::BoundSized,
-                                                 span)
-    }
+    if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant {
+        let is_unit =
+            |var: &hir::Variant| match var.data {
+                hir::VariantData::Unit(..) => true,
+                _ => false
+            };
 
-    pub fn register_builtin_bound(&self,
-                                  ty: Ty<'tcx>,
-                                  builtin_bound: ty::BuiltinBound,
-                                  cause: traits::ObligationCause<'tcx>)
-    {
-        self.inh.fulfillment_cx.borrow_mut()
-            .register_builtin_bound(self.infcx(), ty, builtin_bound, cause);
-    }
+        let has_disr = |var: &hir::Variant| var.disr_expr.is_some();
+        let has_non_units = vs.iter().any(|var| !is_unit(var));
+        let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var));
+        let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var));
 
-    pub fn register_predicate(&self,
-                              obligation: traits::PredicateObligation<'tcx>)
-    {
-        debug!("register_predicate({})",
-               obligation.repr(self.tcx()));
+        if disr_non_unit || (disr_units && has_non_units) {
+            let mut err = struct_span_err!(tcx.sess, sp, E0732,
+                                           "`#[repr(inttype)]` must be specified");
+            err.emit();
+        }
+    }
 
-        self.inh.fulfillment_cx
-            .borrow_mut()
-            .register_predicate_obligation(self.infcx(), obligation);
+    let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len());
+    for ((_, discr), v) in def.discriminants(tcx).zip(vs) {
+        // Check for duplicate discriminant values
+        if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) {
+            let variant_did = def.variants[VariantIdx::new(i)].def_id;
+            let variant_i_hir_id = tcx.hir().as_local_hir_id(variant_did).unwrap();
+            let variant_i = tcx.hir().expect_variant(variant_i_hir_id);
+            let i_span = match variant_i.disr_expr {
+                Some(ref expr) => tcx.hir().span(expr.hir_id),
+                None => tcx.hir().span(variant_i_hir_id)
+            };
+            let span = match v.disr_expr {
+                Some(ref expr) => tcx.hir().span(expr.hir_id),
+                None => v.span
+            };
+            struct_span_err!(tcx.sess, span, E0081,
+                             "discriminant value `{}` already exists", disr_vals[i])
+                .span_label(i_span, format!("first use of `{}`", disr_vals[i]))
+                .span_label(span , format!("enum already has `{}`", disr_vals[i]))
+                .emit();
+        }
+        disr_vals.push(discr);
     }
 
-    pub fn to_ty(&self, ast_t: &ast::Ty) -> Ty<'tcx> {
-        let t = ast_ty_to_ty(self, self, ast_t);
+    check_representable(tcx, sp, def_id);
+    check_transparent(tcx, sp, def_id);
+}
 
-        let mut bounds_checker = wf::BoundsChecker::new(self,
-                                                        ast_t.span,
-                                                        CodeExtent::from_node_id(self.body_id),
-                                                        None);
-        bounds_checker.check_ty(t);
+fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span, qpath: &QPath) {
+    span_err!(tcx.sess, span, E0533,
+              "expected unit struct, unit variant or constant, found {} `{}`",
+              res.descr(),
+              hir::print::to_string(tcx.hir(), |s| s.print_qpath(qpath, false)));
+}
 
-        t
+impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn item_def_id(&self) -> Option<DefId> {
+        None
+    }
+
+    fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
+        let tcx = self.tcx;
+        let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+        let item_id = tcx.hir().ty_param_owner(hir_id);
+        let item_def_id = tcx.hir().local_def_id(item_id);
+        let generics = tcx.generics_of(item_def_id);
+        let index = generics.param_def_id_to_index[&def_id];
+        ty::GenericPredicates {
+            parent: None,
+            predicates: tcx.arena.alloc_from_iter(
+                self.param_env.caller_bounds.iter().filter_map(|&predicate| match predicate {
+                    ty::Predicate::Trait(ref data)
+                    if data.skip_binder().self_ty().is_param(index) => {
+                        // HACK(eddyb) should get the original `Span`.
+                        let span = tcx.def_span(def_id);
+                        Some((predicate, span))
+                    }
+                    _ => None
+                }),
+            ),
+        }
     }
 
-    pub fn pat_to_string(&self, pat: &ast::Pat) -> String {
-        pat.repr(self.tcx())
+    fn re_infer(
+        &self,
+        def: Option<&ty::GenericParamDef>,
+        span: Span,
+    ) -> Option<ty::Region<'tcx>> {
+        let v = match def {
+            Some(def) => infer::EarlyBoundRegion(span, def.name),
+            None => infer::MiscVariable(span)
+        };
+        Some(self.next_region_var(v))
     }
 
-    pub fn expr_ty(&self, ex: &ast::Expr) -> Ty<'tcx> {
-        match self.inh.node_types.borrow().get(&ex.id) {
-            Some(&t) => t,
-            None => {
-                self.tcx().sess.bug(&format!("no type for expr in fcx {}",
-                                            self.tag())[]);
+    fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
+        if let Some(param) = param {
+            if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() {
+                return ty;
             }
+            unreachable!()
+        } else {
+            self.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::TypeInference,
+                span,
+            })
         }
     }
 
-    /// Apply `adjustment` to the type of `expr`
-    pub fn adjust_expr_ty(&self,
-                          expr: &ast::Expr,
-                          adjustment: Option<&ty::AutoAdjustment<'tcx>>)
-                          -> Ty<'tcx>
-    {
-        let raw_ty = self.expr_ty(expr);
-        let raw_ty = self.infcx().shallow_resolve(raw_ty);
-        ty::adjust_ty(self.tcx(),
-                      expr.span,
-                      expr.id,
-                      raw_ty,
-                      adjustment,
-                      |method_call| self.inh.method_map.borrow()
-                                                       .get(&method_call)
-                                                       .map(|method| method.ty))
-    }
-
-    pub fn node_ty(&self, id: ast::NodeId) -> Ty<'tcx> {
-        match self.inh.node_types.borrow().get(&id) {
-            Some(&t) => t,
-            None => {
-                self.tcx().sess.bug(
-                    &format!("no type for node {}: {} in fcx {}",
-                            id, self.tcx().map.node_to_string(id),
-                            self.tag())[]);
+    fn ct_infer(
+        &self,
+        ty: Ty<'tcx>,
+        param: Option<&ty::GenericParamDef>,
+        span: Span,
+    ) -> &'tcx Const<'tcx> {
+        if let Some(param) = param {
+            if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
+                return ct;
             }
+            unreachable!()
+        } else {
+            self.next_const_var(ty, ConstVariableOrigin {
+                kind: ConstVariableOriginKind::ConstInference,
+                span,
+            })
         }
     }
 
-    pub fn item_substs(&self) -> Ref<NodeMap<ty::ItemSubsts<'tcx>>> {
-        self.inh.item_substs.borrow()
+    fn projected_ty_from_poly_trait_ref(&self,
+                                        span: Span,
+                                        item_def_id: DefId,
+                                        poly_trait_ref: ty::PolyTraitRef<'tcx>)
+                                        -> Ty<'tcx>
+    {
+        let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars(
+            span,
+            infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id),
+            &poly_trait_ref
+        );
+
+        self.tcx().mk_projection(item_def_id, trait_ref.substs)
     }
 
-    pub fn opt_node_ty_substs<F>(&self,
-                                 id: ast::NodeId,
-                                 f: F) where
-        F: FnOnce(&ty::ItemSubsts<'tcx>),
-    {
-        match self.inh.item_substs.borrow().get(&id) {
-            Some(s) => { f(s) }
-            None => { }
-        }
-    }
-
-    pub fn mk_subty(&self,
-                    a_is_expected: bool,
-                    origin: infer::TypeOrigin,
-                    sub: Ty<'tcx>,
-                    sup: Ty<'tcx>)
-                    -> Result<(), ty::type_err<'tcx>> {
-        infer::mk_subty(self.infcx(), a_is_expected, origin, sub, sup)
-    }
-
-    pub fn mk_assignty(&self,
-                       expr: &ast::Expr,
-                       sub: Ty<'tcx>,
-                       sup: Ty<'tcx>)
-                       -> Result<(), ty::type_err<'tcx>> {
-        match infer::mk_coercety(self.infcx(),
-                                 false,
-                                 infer::ExprAssignable(expr.span),
-                                 sub,
-                                 sup) {
-            Ok(None) => Ok(()),
-            Err(ref e) => Err((*e)),
-            Ok(Some(adjustment)) => {
-                self.write_adjustment(expr.id, expr.span, adjustment);
-                Ok(())
-            }
+    fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if ty.has_escaping_bound_vars() {
+            ty // FIXME: normalization and escaping regions
+        } else {
+            self.normalize_associated_types_in(span, &ty)
         }
     }
 
-    pub fn mk_eqty(&self,
-                   a_is_expected: bool,
-                   origin: infer::TypeOrigin,
-                   sub: Ty<'tcx>,
-                   sup: Ty<'tcx>)
-                   -> Result<(), ty::type_err<'tcx>> {
-        infer::mk_eqty(self.infcx(), a_is_expected, origin, sub, sup)
+    fn set_tainted_by_errors(&self) {
+        self.infcx.set_tainted_by_errors()
     }
 
-    pub fn mk_subr(&self,
-                   origin: infer::SubregionOrigin<'tcx>,
-                   sub: ty::Region,
-                   sup: ty::Region) {
-        infer::mk_subr(self.infcx(), origin, sub, sup)
+    fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) {
+        self.write_ty(hir_id, ty)
     }
+}
 
-    pub fn type_error_message<M>(&self,
-                                 sp: Span,
-                                 mk_msg: M,
-                                 actual_ty: Ty<'tcx>,
-                                 err: Option<&ty::type_err<'tcx>>) where
-        M: FnOnce(String) -> String,
-    {
-        self.infcx().type_error_message(sp, mk_msg, actual_ty, err);
+/// Controls whether the arguments are tupled. This is used for the call
+/// operator.
+///
+/// Tupling means that all call-side arguments are packed into a tuple and
+/// passed as a single parameter. For example, if tupling is enabled, this
+/// function:
+///
+///     fn f(x: (isize, isize))
+///
+/// Can be called as:
+///
+///     f(1, 2);
+///
+/// Instead of:
+///
+///     f((1, 2));
+#[derive(Clone, Eq, PartialEq)]
+enum TupleArgumentsFlag {
+    DontTupleArguments,
+    TupleArguments,
+}
+
+/// Controls how we perform fallback for unconstrained
+/// type variables.
+enum FallbackMode {
+    /// Do not fallback type variables to opaque types.
+    NoOpaque,
+    /// Perform all possible kinds of fallback, including
+    /// turning type variables to opaque types.
+    All,
+}
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+    pub fn new(
+        inh: &'a Inherited<'a, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        body_id: hir::HirId,
+    ) -> FnCtxt<'a, 'tcx> {
+        FnCtxt {
+            body_id,
+            param_env,
+            err_count_on_creation: inh.tcx.sess.err_count(),
+            ret_coercion: None,
+            ret_coercion_span: RefCell::new(None),
+            yield_ty: None,
+            ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal,
+                                                     hir::CRATE_HIR_ID)),
+            diverges: Cell::new(Diverges::Maybe),
+            has_errors: Cell::new(false),
+            enclosing_breakables: RefCell::new(EnclosingBreakables {
+                stack: Vec::new(),
+                by_id: Default::default(),
+            }),
+            inh,
+        }
     }
 
-    pub fn report_mismatched_types(&self,
-                                   sp: Span,
-                                   e: Ty<'tcx>,
-                                   a: Ty<'tcx>,
-                                   err: &ty::type_err<'tcx>) {
-        self.infcx().report_mismatched_types(sp, e, a, err)
+    pub fn sess(&self) -> &Session {
+        &self.tcx.sess
+    }
+
+    pub fn errors_reported_since_creation(&self) -> bool {
+        self.tcx.sess.err_count() > self.err_count_on_creation
+    }
+
+    /// Produces warning on the given node, if the current point in the
+    /// function is unreachable, and there hasn't been another warning.
+    fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
+        // FIXME: Combine these two 'if' expressions into one once
+        // let chains are implemented
+        if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
+            // If span arose from a desugaring of `if` or `while`, then it is the condition itself,
+            // which diverges, that we are about to lint on. This gives suboptimal diagnostics.
+            // Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
+            if !span.is_desugaring(DesugaringKind::CondTemporary) &&
+                !span.is_desugaring(DesugaringKind::Async) &&
+                !orig_span.is_desugaring(DesugaringKind::Await)
+            {
+                self.diverges.set(Diverges::WarnedAlways);
+
+                debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
+
+                let msg = format!("unreachable {}", kind);
+                self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg)
+                    .span_label(span, &msg)
+                    .span_label(
+                        orig_span,
+                        custom_note.unwrap_or("any code following this expression is unreachable"),
+                    )
+                    .emit();
+            }
+        }
     }
 
-    /// Registers an obligation for checking later, during regionck, that the type `ty` must
-    /// outlive the region `r`.
-    pub fn register_region_obligation(&self,
-                                      ty: Ty<'tcx>,
-                                      region: ty::Region,
-                                      cause: traits::ObligationCause<'tcx>)
-    {
-        let mut fulfillment_cx = self.inh.fulfillment_cx.borrow_mut();
-        fulfillment_cx.register_region_obligation(self.infcx(), ty, region, cause);
+    pub fn cause(&self,
+                 span: Span,
+                 code: ObligationCauseCode<'tcx>)
+                 -> ObligationCause<'tcx> {
+        ObligationCause::new(span, self.body_id, code)
     }
 
-    pub fn add_default_region_param_bounds(&self,
-                                           substs: &Substs<'tcx>,
-                                           expr: &ast::Expr)
-    {
-        for &ty in substs.types.iter() {
-            let default_bound = ty::ReScope(CodeExtent::from_node_id(expr.id));
-            let cause = traits::ObligationCause::new(expr.span, self.body_id,
-                                                     traits::MiscObligation);
-            self.register_region_obligation(ty, default_bound, cause);
-        }
+    pub fn misc(&self, span: Span) -> ObligationCause<'tcx> {
+        self.cause(span, ObligationCauseCode::MiscObligation)
     }
 
-    /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
-    /// type/region parameter was instantiated (`substs`), creates and registers suitable
-    /// trait/region obligations.
-    ///
-    /// For example, if there is a function:
-    ///
-    /// ```
-    /// fn foo<'a,T:'a>(...)
-    /// ```
-    ///
-    /// and a reference:
-    ///
-    /// ```
-    /// let f = foo;
-    /// ```
-    ///
-    /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
-    /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
-    pub fn add_obligations_for_parameters(&self,
-                                          cause: traits::ObligationCause<'tcx>,
-                                          generic_bounds: &ty::GenericBounds<'tcx>)
-    {
-        assert!(!generic_bounds.has_escaping_regions());
+    /// Resolves type and const variables in `ty` if possible. Unlike the infcx
+    /// version (resolve_vars_if_possible), this version will
+    /// also select obligations if it seems useful, in an effort
+    /// to get more type information.
+    fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
+        debug!("resolve_vars_with_obligations(ty={:?})", ty);
+
+        // No Infer()? Nothing needs doing.
+        if !ty.has_infer_types() && !ty.has_infer_consts() {
+            debug!("resolve_vars_with_obligations: ty={:?}", ty);
+            return ty;
+        }
 
-        debug!("add_obligations_for_parameters(generic_bounds={})",
-               generic_bounds.repr(self.tcx()));
+        // If `ty` is a type variable, see whether we already know what it is.
+        ty = self.resolve_vars_if_possible(&ty);
+        if !ty.has_infer_types() && !ty.has_infer_consts()  {
+            debug!("resolve_vars_with_obligations: ty={:?}", ty);
+            return ty;
+        }
 
-        let obligations = traits::predicates_for_generics(self.tcx(),
-                                                          cause,
-                                                          generic_bounds);
+        // If not, try resolving pending obligations as much as
+        // possible. This can help substantially when there are
+        // indirect dependencies that don't seem worth tracking
+        // precisely.
+        self.select_obligations_where_possible(false, |_| {});
+        ty = self.resolve_vars_if_possible(&ty);
 
-        obligations.map_move(|o| self.register_predicate(o));
+        debug!("resolve_vars_with_obligations: ty={:?}", ty);
+        ty
     }
-}
 
-impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> {
-    fn default_region_bound(&self, span: Span) -> Option<ty::Region> {
-        Some(self.infcx().next_region_var(infer::MiscVariable(span)))
+    fn record_deferred_call_resolution(
+        &self,
+        closure_def_id: DefId,
+        r: DeferredCallResolution<'tcx>,
+    ) {
+        let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
+        deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
     }
 
-    fn anon_regions(&self, span: Span, count: uint)
-                    -> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>> {
-        Ok(range(0, count).map(|_| {
-            self.infcx().next_region_var(infer::MiscVariable(span))
-        }).collect())
+    fn remove_deferred_call_resolutions(
+        &self,
+        closure_def_id: DefId,
+    ) -> Vec<DeferredCallResolution<'tcx>> {
+        let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
+        deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![])
     }
-}
 
-#[derive(Copy, Show, PartialEq, Eq)]
-pub enum LvaluePreference {
-    PreferMutLvalue,
-    NoPreference
-}
+    pub fn tag(&self) -> String {
+        format!("{:p}", self)
+    }
 
-/// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop` to decide
-/// whether to terminate the loop. Returns the final type and number of derefs that it performed.
-///
-/// Note: this method does not modify the adjustments table. The caller is responsible for
-/// inserting an AutoAdjustment record into the `fcx` using one of the suitable methods.
-pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
-                                 sp: Span,
-                                 base_ty: Ty<'tcx>,
-                                 opt_expr: Option<&ast::Expr>,
-                                 mut lvalue_pref: LvaluePreference,
-                                 mut should_stop: F)
-                                 -> (Ty<'tcx>, uint, Option<T>)
-    where F: FnMut(Ty<'tcx>, uint) -> Option<T>,
-{
-    debug!("autoderef(base_ty={}, opt_expr={}, lvalue_pref={:?})",
-           base_ty.repr(fcx.tcx()),
-           opt_expr.repr(fcx.tcx()),
-           lvalue_pref);
-
-    let mut t = base_ty;
-    for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) {
-        let resolved_t = structurally_resolved_type(fcx, sp, t);
-
-        if ty::type_is_error(resolved_t) {
-            return (resolved_t, autoderefs, None);
-        }
-
-        match should_stop(resolved_t, autoderefs) {
-            Some(x) => return (resolved_t, autoderefs, Some(x)),
-            None => {}
-        }
-
-        // Otherwise, deref if type is derefable:
-        let mt = match ty::deref(resolved_t, false) {
-            Some(mt) => Some(mt),
-            None => {
-                let method_call = opt_expr.map(|expr| MethodCall::autoderef(expr.id, autoderefs));
-
-                // Super subtle: it might seem as though we should
-                // pass `opt_expr` to `try_overloaded_deref`, so that
-                // the (implicit) autoref of using an overloaded deref
-                // would get added to the adjustment table. However we
-                // do not do that, because it's kind of a
-                // "meta-adjustment" -- instead, we just leave it
-                // unrecorded and know that there "will be" an
-                // autoref. regionck and other bits of the code base,
-                // when they encounter an overloaded autoderef, have
-                // to do some reconstructive surgery. This is a pretty
-                // complex mess that is begging for a proper MIR.
-                try_overloaded_deref(fcx, sp, method_call, None, resolved_t, lvalue_pref)
-            }
-        };
-        match mt {
-            Some(mt) => {
-                t = mt.ty;
-                if mt.mutbl == ast::MutImmutable {
-                    lvalue_pref = NoPreference;
-                }
-            }
-            None => return (resolved_t, autoderefs, None)
+    pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
+        self.locals.borrow().get(&nid).cloned().unwrap_or_else(||
+            span_bug!(span, "no type for local variable {}",
+                      self.tcx.hir().node_to_string(nid))
+        )
+    }
+
+    #[inline]
+    pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) {
+        debug!("write_ty({:?}, {:?}) in fcx {}",
+               id, self.resolve_vars_if_possible(&ty), self.tag());
+        self.tables.borrow_mut().node_types_mut().insert(id, ty);
+
+        if ty.references_error() {
+            self.has_errors.set(true);
+            self.set_tainted_by_errors();
         }
     }
 
-    // We've reached the recursion limit, error gracefully.
-    span_err!(fcx.tcx().sess, sp, E0055,
-        "reached the recursion limit while auto-dereferencing {}",
-        base_ty.repr(fcx.tcx()));
-    (fcx.tcx().types.err, 0, None)
-}
+    pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) {
+        self.tables.borrow_mut().field_indices_mut().insert(hir_id, index);
+    }
 
-fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                  span: Span,
-                                  method_call: Option<MethodCall>,
-                                  base_expr: Option<&ast::Expr>,
-                                  base_ty: Ty<'tcx>,
-                                  lvalue_pref: LvaluePreference)
-                                  -> Option<ty::mt<'tcx>>
-{
-    // Try DerefMut first, if preferred.
-    let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) {
-        (PreferMutLvalue, Some(trait_did)) => {
-            method::lookup_in_trait(fcx, span, base_expr,
-                                    token::intern("deref_mut"), trait_did,
-                                    base_ty, None)
-        }
-        _ => None
-    };
+    fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) {
+        self.tables.borrow_mut().type_dependent_defs_mut().insert(hir_id, r);
+    }
 
-    // Otherwise, fall back to Deref.
-    let method = match (method, fcx.tcx().lang_items.deref_trait()) {
-        (None, Some(trait_did)) => {
-            method::lookup_in_trait(fcx, span, base_expr,
-                                    token::intern("deref"), trait_did,
-                                    base_ty, None)
-        }
-        (method, _) => method
-    };
+    pub fn write_method_call(&self,
+                             hir_id: hir::HirId,
+                             method: MethodCallee<'tcx>) {
+        debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
+        self.write_resolution(hir_id, Ok((DefKind::Method, method.def_id)));
+        self.write_substs(hir_id, method.substs);
 
-    make_overloaded_lvalue_return_type(fcx, method_call, method)
-}
+        // When the method is confirmed, the `method.substs` includes
+        // parameters from not just the method, but also the impl of
+        // the method -- in particular, the `Self` type will be fully
+        // resolved. However, those are not something that the "user
+        // specified" -- i.e., those types come from the inferred type
+        // of the receiver, not something the user wrote. So when we
+        // create the user-substs, we want to replace those earlier
+        // types with just the types that the user actually wrote --
+        // that is, those that appear on the *method itself*.
+        //
+        // As an example, if the user wrote something like
+        // `foo.bar::<u32>(...)` -- the `Self` type here will be the
+        // type of `foo` (possibly adjusted), but we don't want to
+        // include that. We want just the `[_, u32]` part.
+        if !method.substs.is_noop() {
+            let method_generics = self.tcx.generics_of(method.def_id);
+            if !method_generics.params.is_empty() {
+                let user_type_annotation = self.infcx.probe(|_| {
+                    let user_substs = UserSubsts {
+                        substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| {
+                            let i = param.index as usize;
+                            if i < method_generics.parent_count {
+                                self.infcx.var_for_def(DUMMY_SP, param)
+                            } else {
+                                method.substs[i]
+                            }
+                        }),
+                        user_self_ty: None, // not relevant here
+                    };
 
-/// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait returns a type of `&T`, but the
-/// actual type we assign to the *expression* is `T`. So this function just peels off the return
-/// type by one layer to yield `T`. It also inserts the `method-callee` into the method map.
-fn make_overloaded_lvalue_return_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                method_call: Option<MethodCall>,
-                                                method: Option<MethodCallee<'tcx>>)
-                                                -> Option<ty::mt<'tcx>>
-{
-    match method {
-        Some(method) => {
-            let ref_ty = // invoked methods have all LB regions instantiated
-                ty::assert_no_late_bound_regions(
-                    fcx.tcx(), &ty::ty_fn_ret(method.ty));
-            match method_call {
-                Some(method_call) => {
-                    fcx.inh.method_map.borrow_mut().insert(method_call,
-                                                           method);
-                }
-                None => {}
-            }
-            match ref_ty {
-                ty::FnConverging(ref_ty) => {
-                    ty::deref(ref_ty, true)
-                }
-                ty::FnDiverging => {
-                    fcx.tcx().sess.bug("index/deref traits do not define a `!` return")
-                }
+                    self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf(
+                        method.def_id,
+                        user_substs,
+                    ))
+                });
+
+                debug!("write_method_call: user_type_annotation={:?}", user_type_annotation);
+                self.write_user_type_annotation(hir_id, user_type_annotation);
             }
         }
-        None => None,
     }
-}
-
-fn autoderef_for_index<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
-                                       base_expr: &ast::Expr,
-                                       base_ty: Ty<'tcx>,
-                                       lvalue_pref: LvaluePreference,
-                                       mut step: F)
-                                       -> Option<T> where
-    F: FnMut(Ty<'tcx>, ty::AutoDerefRef<'tcx>) -> Option<T>,
-{
-    // FIXME(#18741) -- this is almost but not quite the same as the
-    // autoderef that normal method probing does. They could likely be
-    // consolidated.
-
-    let (ty, autoderefs, final_mt) =
-        autoderef(fcx, base_expr.span, base_ty, Some(base_expr), lvalue_pref, |adj_ty, idx| {
-            let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None };
-            step(adj_ty, autoderefref)
-        });
 
-    if final_mt.is_some() {
-        return final_mt;
-    }
+    pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
+        if !substs.is_noop() {
+            debug!("write_substs({:?}, {:?}) in fcx {}",
+                   node_id,
+                   substs,
+                   self.tag());
 
-    // After we have fully autoderef'd, if the resulting type is [T, ..n], then
-    // do a final unsized coercion to yield [T].
-    match ty.sty {
-        ty::ty_vec(element_ty, Some(n)) => {
-            let adjusted_ty = ty::mk_vec(fcx.tcx(), element_ty, None);
-            let autoderefref = ty::AutoDerefRef {
-                autoderefs: autoderefs,
-                autoref: Some(ty::AutoUnsize(ty::UnsizeLength(n)))
-            };
-            step(adjusted_ty, autoderefref)
-        }
-        _ => {
-            None
+            self.tables.borrow_mut().node_substs_mut().insert(node_id, substs);
         }
     }
-}
 
-/// To type-check `base_expr[index_expr]`, we progressively autoderef (and otherwise adjust)
-/// `base_expr`, looking for a type which either supports builtin indexing or overloaded indexing.
-/// This loop implements one step in that search; the autoderef loop is implemented by
-/// `autoderef_for_index`.
-fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                            method_call: MethodCall,
-                            expr: &ast::Expr,
-                            base_expr: &ast::Expr,
-                            adjusted_ty: Ty<'tcx>,
-                            adjustment: ty::AutoDerefRef<'tcx>,
-                            lvalue_pref: LvaluePreference,
-                            index_ty: Ty<'tcx>)
-                            -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
-{
-    let tcx = fcx.tcx();
-    debug!("try_index_step(expr={}, base_expr.id={}, adjusted_ty={}, adjustment={:?}, index_ty={})",
-           expr.repr(tcx),
-           base_expr.repr(tcx),
-           adjusted_ty.repr(tcx),
-           adjustment,
-           index_ty.repr(tcx));
-
-    let input_ty = fcx.infcx().next_ty_var();
-
-    // First, try built-in indexing.
-    match (ty::index(adjusted_ty), &index_ty.sty) {
-        (Some(ty), &ty::ty_uint(ast::TyUs(_))) | (Some(ty), &ty::ty_infer(ty::IntVar(_))) => {
-            debug!("try_index_step: success, using built-in indexing");
-            fcx.write_adjustment(base_expr.id, base_expr.span, ty::AdjustDerefRef(adjustment));
-            return Some((tcx.types.uint, ty));
-        }
-        _ => {}
-    }
-
-    // Try `IndexMut` first, if preferred.
-    let method = match (lvalue_pref, tcx.lang_items.index_mut_trait()) {
-        (PreferMutLvalue, Some(trait_did)) => {
-            method::lookup_in_trait_adjusted(fcx,
-                                             expr.span,
-                                             Some(&*base_expr),
-                                             token::intern("index_mut"),
-                                             trait_did,
-                                             adjustment.clone(),
-                                             adjusted_ty,
-                                             Some(vec![input_ty]))
-        }
-        _ => None,
-    };
-
-    // Otherwise, fall back to `Index`.
-    let method = match (method, tcx.lang_items.index_trait()) {
-        (None, Some(trait_did)) => {
-            method::lookup_in_trait_adjusted(fcx,
-                                             expr.span,
-                                             Some(&*base_expr),
-                                             token::intern("index"),
-                                             trait_did,
-                                             adjustment.clone(),
-                                             adjusted_ty,
-                                             Some(vec![input_ty]))
-        }
-        (method, _) => method,
-    };
-
-    // If some lookup succeeds, write callee into table and extract index/element
-    // type from the method signature.
-    // If some lookup succeeded, install method in table
-    method.and_then(|method| {
-        debug!("try_index_step: success, using overloaded indexing");
-        make_overloaded_lvalue_return_type(fcx, Some(method_call), Some(method)).
-            map(|ret| (input_ty, ret.ty))
-    })
-}
+    /// Given the substs that we just converted from the HIR, try to
+    /// canonicalize them and store them as user-given substitutions
+    /// (i.e., substitutions that must be respected by the NLL check).
+    ///
+    /// This should be invoked **before any unifications have
+    /// occurred**, so that annotations like `Vec<_>` are preserved
+    /// properly.
+    pub fn write_user_type_annotation_from_substs(
+        &self,
+        hir_id: hir::HirId,
+        def_id: DefId,
+        substs: SubstsRef<'tcx>,
+        user_self_ty: Option<UserSelfTy<'tcx>>,
+    ) {
+        debug!(
+            "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
+             user_self_ty={:?} in fcx {}",
+            hir_id, def_id, substs, user_self_ty, self.tag(),
+        );
 
-/// Given the head of a `for` expression, looks up the `next` method in the
-/// `Iterator` trait. Panics if the expression does not implement `next`.
-///
-/// The return type of this function represents the concrete element type
-/// `A` in the type `Iterator<A>` that the method returns.
-fn lookup_method_for_for_loop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                        iterator_expr: &ast::Expr,
-                                        loop_id: ast::NodeId)
-                                        -> Ty<'tcx> {
-    let trait_did = match fcx.tcx().lang_items.require(IteratorItem) {
-        Ok(trait_did) => trait_did,
-        Err(ref err_string) => {
-            fcx.tcx().sess.span_err(iterator_expr.span,
-                                    &err_string[]);
-            return fcx.tcx().types.err
-        }
-    };
+        if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) {
+            let canonicalized = self.infcx.canonicalize_user_type_annotation(
+                &UserType::TypeOf(def_id, UserSubsts {
+                    substs,
+                    user_self_ty,
+                })
+            );
+            debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
+            self.write_user_type_annotation(hir_id, canonicalized);
+        }
+    }
+
+    pub fn write_user_type_annotation(
+        &self,
+        hir_id: hir::HirId,
+        canonical_user_type_annotation: CanonicalUserType<'tcx>,
+    ) {
+        debug!(
+            "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
+            hir_id, canonical_user_type_annotation, self.tag(),
+        );
 
-    let expr_type = fcx.expr_ty(&*iterator_expr);
-    let method = method::lookup_in_trait(fcx,
-                                         iterator_expr.span,
-                                         Some(&*iterator_expr),
-                                         token::intern("next"),
-                                         trait_did,
-                                         expr_type,
-                                         None);
-
-    // Regardless of whether the lookup succeeds, check the method arguments
-    // so that we have *some* type for each argument.
-    let method_type = match method {
-        Some(ref method) => method.ty,
-        None => {
-            let true_expr_type = fcx.infcx().resolve_type_vars_if_possible(&expr_type);
-
-            if !ty::type_is_error(true_expr_type) {
-                let ty_string = fcx.infcx().ty_to_string(true_expr_type);
-                fcx.tcx().sess.span_err(iterator_expr.span,
-                                        &format!("`for` loop expression has type `{}` which does \
-                                                not implement the `Iterator` trait; \
-                                                maybe try .iter()",
-                                                ty_string)[]);
-            }
-            fcx.tcx().types.err
-        }
-    };
-    let return_type = check_method_argument_types(fcx,
-                                                  iterator_expr.span,
-                                                  method_type,
-                                                  iterator_expr,
-                                                  &[],
-                                                  AutorefArgs::No,
-                                                  DontTupleArguments);
-
-    match method {
-        Some(method) => {
-            fcx.inh.method_map.borrow_mut().insert(MethodCall::expr(loop_id),
-                                                   method);
-
-            // We expect the return type to be `Option` or something like it.
-            // Grab the first parameter of its type substitution.
-            let return_type = match return_type {
-                ty::FnConverging(return_type) =>
-                    structurally_resolved_type(fcx, iterator_expr.span, return_type),
-                ty::FnDiverging => fcx.tcx().types.err
-            };
-            match return_type.sty {
-                ty::ty_enum(_, ref substs)
-                        if !substs.types.is_empty_in(subst::TypeSpace) => {
-                    *substs.types.get(subst::TypeSpace, 0)
-                }
-                ty::ty_err => {
-                    fcx.tcx().types.err
-                }
-                _ => {
-                    fcx.tcx().sess.span_err(iterator_expr.span,
-                                            &format!("`next` method of the `Iterator` \
-                                                    trait has an unexpected type `{}`",
-                                                    fcx.infcx().ty_to_string(return_type))
-                                            []);
-                    fcx.tcx().types.err
-                }
-            }
+        if !canonical_user_type_annotation.is_identity() {
+            self.tables.borrow_mut().user_provided_types_mut().insert(
+                hir_id, canonical_user_type_annotation
+            );
+        } else {
+            debug!("write_user_type_annotation: skipping identity substs");
         }
-        None => fcx.tcx().types.err
     }
-}
 
-fn check_method_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                         sp: Span,
-                                         method_fn_ty: Ty<'tcx>,
-                                         callee_expr: &ast::Expr,
-                                         args_no_rcvr: &[&P<ast::Expr>],
-                                         autoref_args: AutorefArgs,
-                                         tuple_arguments: TupleArgumentsFlag)
-                                         -> ty::FnOutput<'tcx> {
-    if ty::type_is_error(method_fn_ty) {
-        let err_inputs = err_args(fcx.tcx(), args_no_rcvr.len());
-
-        let err_inputs = match tuple_arguments {
-            DontTupleArguments => err_inputs,
-            TupleArguments => vec![ty::mk_tup(fcx.tcx(), err_inputs)],
-        };
+    pub fn apply_adjustments(&self, expr: &hir::Expr, adj: Vec<Adjustment<'tcx>>) {
+        debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
 
-        check_argument_types(fcx,
-                             sp,
-                             &err_inputs[],
-                             args_no_rcvr,
-                             autoref_args,
-                             false,
-                             tuple_arguments);
-        ty::FnConverging(fcx.tcx().types.err)
-    } else {
-        match method_fn_ty.sty {
-            ty::ty_bare_fn(_, ref fty) => {
-                // HACK(eddyb) ignore self in the definition (see above).
-                check_argument_types(fcx,
-                                     sp,
-                                     fty.sig.0.inputs.slice_from(1),
-                                     args_no_rcvr,
-                                     autoref_args,
-                                     fty.sig.0.variadic,
-                                     tuple_arguments);
-                fty.sig.0.output
-            }
-            _ => {
-                fcx.tcx().sess.span_bug(callee_expr.span,
-                                        "method without bare fn type");
-            }
+        if adj.is_empty() {
+            return;
         }
-    }
-}
 
-/// Generic function that factors out common logic from function calls, method calls and overloaded
-/// operators.
-fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                  sp: Span,
-                                  fn_inputs: &[Ty<'tcx>],
-                                  args: &[&P<ast::Expr>],
-                                  autoref_args: AutorefArgs,
-                                  variadic: bool,
-                                  tuple_arguments: TupleArgumentsFlag) {
-    let tcx = fcx.ccx.tcx;
-
-    // Grab the argument types, supplying fresh type variables
-    // if the wrong number of arguments were supplied
-    let supplied_arg_count = if tuple_arguments == DontTupleArguments {
-        args.len()
-    } else {
-        1
-    };
-
-    let expected_arg_count = fn_inputs.len();
-    let formal_tys = if tuple_arguments == TupleArguments {
-        let tuple_type = structurally_resolved_type(fcx, sp, fn_inputs[0]);
-        match tuple_type.sty {
-            ty::ty_tup(ref arg_types) => {
-                if arg_types.len() != args.len() {
-                    span_err!(tcx.sess, sp, E0057,
-                        "this function takes {} parameter{} but {} parameter{} supplied",
-                        arg_types.len(),
-                        if arg_types.len() == 1 {""} else {"s"},
-                        args.len(),
-                        if args.len() == 1 {" was"} else {"s were"});
-                    err_args(fcx.tcx(), args.len())
-                } else {
-                    (*arg_types).clone()
-                }
-            }
-            _ => {
-                span_err!(tcx.sess, sp, E0059,
-                    "cannot use call notation; the first type parameter \
-                     for the function trait is neither a tuple nor unit");
-                err_args(fcx.tcx(), args.len())
-            }
-        }
-    } else if expected_arg_count == supplied_arg_count {
-        fn_inputs.iter().map(|a| *a).collect()
-    } else if variadic {
-        if supplied_arg_count >= expected_arg_count {
-            fn_inputs.iter().map(|a| *a).collect()
-        } else {
-            span_err!(tcx.sess, sp, E0060,
-                "this function takes at least {} parameter{} \
-                 but {} parameter{} supplied",
-                expected_arg_count,
-                if expected_arg_count == 1 {""} else {"s"},
-                supplied_arg_count,
-                if supplied_arg_count == 1 {" was"} else {"s were"});
-            err_args(fcx.tcx(), supplied_arg_count)
-        }
-    } else {
-        span_err!(tcx.sess, sp, E0061,
-            "this function takes {} parameter{} but {} parameter{} supplied",
-            expected_arg_count,
-            if expected_arg_count == 1 {""} else {"s"},
-            supplied_arg_count,
-            if supplied_arg_count == 1 {" was"} else {"s were"});
-        err_args(fcx.tcx(), supplied_arg_count)
-    };
-
-    debug!("check_argument_types: formal_tys={:?}",
-           formal_tys.iter().map(|t| fcx.infcx().ty_to_string(*t)).collect::<Vec<String>>());
-
-    // Check the arguments.
-    // We do this in a pretty awful way: first we typecheck any arguments
-    // that are not anonymous functions, then we typecheck the anonymous
-    // functions. This is so that we have more information about the types
-    // of arguments when we typecheck the functions. This isn't really the
-    // right way to do this.
-    let xs = [false, true];
-    for check_blocks in xs.iter() {
-        let check_blocks = *check_blocks;
-        debug!("check_blocks={}", check_blocks);
-
-        // More awful hacks: before we check the blocks, try to do
-        // an "opportunistic" vtable resolution of any trait
-        // bounds on the call.
-        if check_blocks {
-            vtable::select_new_fcx_obligations(fcx);
-        }
-
-        // For variadic functions, we don't have a declared type for all of
-        // the arguments hence we only do our usual type checking with
-        // the arguments who's types we do know.
-        let t = if variadic {
-            expected_arg_count
-        } else if tuple_arguments == TupleArguments {
-            args.len()
-        } else {
-            supplied_arg_count
-        };
-        for (i, arg) in args.iter().take(t).enumerate() {
-            let is_block = match arg.node {
-                ast::ExprClosure(..) => true,
-                _ => false
-            };
-
-            if is_block == check_blocks {
-                debug!("checking the argument");
-                let mut formal_ty = formal_tys[i];
-
-                match autoref_args {
-                    AutorefArgs::Yes => {
-                        match formal_ty.sty {
-                            ty::ty_rptr(_, mt) => formal_ty = mt.ty,
-                            ty::ty_err => (),
-                            _ => {
-                                // So we hit this case when one implements the
-                                // operator traits but leaves an argument as
-                                // just T instead of &T. We'll catch it in the
-                                // mismatch impl/trait method phase no need to
-                                // ICE here.
-                                // See: #11450
-                                formal_ty = tcx.types.err;
-                            }
-                        }
+        match self.tables.borrow_mut().adjustments_mut().entry(expr.hir_id) {
+            Entry::Vacant(entry) => { entry.insert(adj); },
+            Entry::Occupied(mut entry) => {
+                debug!(" - composing on top of {:?}", entry.get());
+                match (&entry.get()[..], &adj[..]) {
+                    // Applying any adjustment on top of a NeverToAny
+                    // is a valid NeverToAny adjustment, because it can't
+                    // be reached.
+                    (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
+                    (&[
+                        Adjustment { kind: Adjust::Deref(_), .. },
+                        Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
+                    ], &[
+                        Adjustment { kind: Adjust::Deref(_), .. },
+                        .. // Any following adjustments are allowed.
+                    ]) => {
+                        // A reborrow has no effect before a dereference.
                     }
-                    AutorefArgs::No => {}
-                }
-
-                check_expr_coercable_to_type(fcx, &***arg, formal_ty);
+                    // FIXME: currently we never try to compose autoderefs
+                    // and ReifyFnPointer/UnsafeFnPointer, but we could.
+                    _ =>
+                        bug!("while adjusting {:?}, can't compose {:?} and {:?}",
+                             expr, entry.get(), adj)
+                };
+                *entry.get_mut() = adj;
             }
         }
     }
 
-    // We also need to make sure we at least write the ty of the other
-    // arguments which we skipped above.
-    if variadic {
-        for arg in args.iter().skip(expected_arg_count) {
-            check_expr(fcx, &***arg);
-
-            // There are a few types which get autopromoted when passed via varargs
-            // in C but we just error out instead and require explicit casts.
-            let arg_ty = structurally_resolved_type(fcx, arg.span,
-                                                    fcx.expr_ty(&***arg));
-            match arg_ty.sty {
-                ty::ty_float(ast::TyF32) => {
-                    fcx.type_error_message(arg.span,
-                                           |t| {
-                        format!("can't pass an {} to variadic \
-                                 function, cast to c_double", t)
-                    }, arg_ty, None);
-                }
-                ty::ty_int(ast::TyI8) | ty::ty_int(ast::TyI16) | ty::ty_bool => {
-                    fcx.type_error_message(arg.span, |t| {
-                        format!("can't pass {} to variadic \
-                                 function, cast to c_int",
-                                       t)
-                    }, arg_ty, None);
-                }
-                ty::ty_uint(ast::TyU8) | ty::ty_uint(ast::TyU16) => {
-                    fcx.type_error_message(arg.span, |t| {
-                        format!("can't pass {} to variadic \
-                                 function, cast to c_uint",
-                                       t)
-                    }, arg_ty, None);
-                }
-                _ => {}
-            }
-        }
+    /// Basically whenever we are converting from a type scheme into
+    /// the fn body space, we always want to normalize associated
+    /// types as well. This function combines the two.
+    fn instantiate_type_scheme<T>(&self,
+                                  span: Span,
+                                  substs: SubstsRef<'tcx>,
+                                  value: &T)
+                                  -> T
+        where T : TypeFoldable<'tcx>
+    {
+        let value = value.subst(self.tcx, substs);
+        let result = self.normalize_associated_types_in(span, &value);
+        debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}",
+               value,
+               substs,
+               result);
+        result
     }
-}
 
-// FIXME(#17596) Ty<'tcx> is incorrectly invariant w.r.t 'tcx.
-fn err_args<'tcx>(tcx: &ty::ctxt<'tcx>, len: uint) -> Vec<Ty<'tcx>> {
-    range(0, len).map(|_| tcx.types.err).collect()
-}
-
-fn write_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                        call_expr: &ast::Expr,
-                        output: ty::FnOutput<'tcx>) {
-    fcx.write_ty(call_expr.id, match output {
-        ty::FnConverging(output_ty) => output_ty,
-        ty::FnDiverging => fcx.infcx().next_diverging_ty_var()
-    });
-}
+    /// As `instantiate_type_scheme`, but for the bounds found in a
+    /// generic type scheme.
+    fn instantiate_bounds(
+        &self,
+        span: Span,
+        def_id: DefId,
+        substs: SubstsRef<'tcx>,
+    ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) {
+        let bounds = self.tcx.predicates_of(def_id);
+        let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect();
+        let result = bounds.instantiate(self.tcx, substs);
+        let result = self.normalize_associated_types_in(span, &result);
+        debug!(
+            "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}",
+               bounds,
+               substs,
+            result,
+            spans,
+        );
+        (result, spans)
+    }
+
+    /// Replaces the opaque types from the given value with type variables,
+    /// and records the `OpaqueTypeMap` for later use during writeback. See
+    /// `InferCtxt::instantiate_opaque_types` for more details.
+    fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
+        &self,
+        parent_id: hir::HirId,
+        value: &T,
+        value_span: Span,
+    ) -> T {
+        let parent_def_id = self.tcx.hir().local_def_id(parent_id);
+        debug!("instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})",
+               parent_def_id,
+               value);
+
+        let (value, opaque_type_map) = self.register_infer_ok_obligations(
+            self.instantiate_opaque_types(
+                parent_def_id,
+                self.body_id,
+                self.param_env,
+                value,
+                value_span,
+            )
+        );
 
-// AST fragment checking
-fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                       lit: &ast::Lit,
-                       expected: Expectation<'tcx>)
-                       -> Ty<'tcx>
-{
-    let tcx = fcx.ccx.tcx;
-
-    match lit.node {
-        ast::LitStr(..) => ty::mk_str_slice(tcx, tcx.mk_region(ty::ReStatic), ast::MutImmutable),
-        ast::LitBinary(..) => {
-            ty::mk_slice(tcx,
-                         tcx.mk_region(ty::ReStatic),
-                         ty::mt{ ty: tcx.types.u8, mutbl: ast::MutImmutable })
-        }
-        ast::LitByte(_) => tcx.types.u8,
-        ast::LitChar(_) => tcx.types.char,
-        ast::LitInt(_, ast::SignedIntLit(t, _)) => ty::mk_mach_int(tcx, t),
-        ast::LitInt(_, ast::UnsignedIntLit(t)) => ty::mk_mach_uint(tcx, t),
-        ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
-            let opt_ty = expected.map_to_option(fcx, |ty| {
-                match ty.sty {
-                    ty::ty_int(_) | ty::ty_uint(_) => Some(ty),
-                    ty::ty_char => Some(tcx.types.u8),
-                    ty::ty_ptr(..) => Some(tcx.types.uint),
-                    ty::ty_bare_fn(..) => Some(tcx.types.uint),
-                    _ => None
-                }
-            });
-            opt_ty.unwrap_or_else(
-                || ty::mk_int_var(tcx, fcx.infcx().next_int_var_id()))
-        }
-        ast::LitFloat(_, t) => ty::mk_mach_float(tcx, t),
-        ast::LitFloatUnsuffixed(_) => {
-            let opt_ty = expected.map_to_option(fcx, |ty| {
-                match ty.sty {
-                    ty::ty_float(_) => Some(ty),
-                    _ => None
-                }
-            });
-            opt_ty.unwrap_or_else(
-                || ty::mk_float_var(tcx, fcx.infcx().next_float_var_id()))
+        let mut opaque_types = self.opaque_types.borrow_mut();
+        let mut opaque_types_vars = self.opaque_types_vars.borrow_mut();
+        for (ty, decl) in opaque_type_map {
+            let _ = opaque_types.insert(ty, decl);
+            let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
         }
-        ast::LitBool(_) => tcx.types.bool
-    }
-}
 
-pub fn valid_range_bounds(ccx: &CrateCtxt,
-                          from: &ast::Expr,
-                          to: &ast::Expr)
-                       -> Option<bool> {
-    match const_eval::compare_lit_exprs(ccx.tcx, from, to) {
-        Some(val) => Some(val <= 0),
-        None => None
+        value
     }
-}
-
-pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                     expr: &ast::Expr,
-                                     expected: Ty<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, ExpectHasType(expected), NoPreference,
-        || demand::suptype(fcx, expr.span, expected, fcx.expr_ty(expr)));
-}
 
-fn check_expr_coercable_to_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                          expr: &ast::Expr,
-                                          expected: Ty<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, ExpectHasType(expected), NoPreference,
-        || demand::coerce(fcx, expr.span, expected, expr));
-}
+    fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
+        where T : TypeFoldable<'tcx>
+    {
+        self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value)
+    }
 
-fn check_expr_with_hint<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &ast::Expr,
-                                  expected: Ty<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, ExpectHasType(expected), NoPreference,
-        || ())
-}
+    fn normalize_associated_types_in_as_infer_ok<T>(&self, span: Span, value: &T)
+                                                    -> InferOk<'tcx, T>
+        where T : TypeFoldable<'tcx>
+    {
+        self.inh.partially_normalize_associated_types_in(span,
+                                                         self.body_id,
+                                                         self.param_env,
+                                                         value)
+    }
 
-fn check_expr_with_expectation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                         expr: &ast::Expr,
-                                         expected: Expectation<'tcx>) {
-    check_expr_with_unifier(
-        fcx, expr, expected, NoPreference,
-        || ())
-}
+    pub fn require_type_meets(&self,
+                              ty: Ty<'tcx>,
+                              span: Span,
+                              code: traits::ObligationCauseCode<'tcx>,
+                              def_id: DefId)
+    {
+        self.register_bound(
+            ty,
+            def_id,
+            traits::ObligationCause::new(span, self.body_id, code));
+    }
 
-fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                         expr: &ast::Expr,
-                                                         expected: Expectation<'tcx>,
-                                                         lvalue_pref: LvaluePreference)
-{
-    check_expr_with_unifier(fcx, expr, expected, lvalue_pref, || ())
-}
+    pub fn require_type_is_sized(
+        &self,
+        ty: Ty<'tcx>,
+        span: Span,
+        code: traits::ObligationCauseCode<'tcx>,
+    ) {
+        if !ty.references_error() {
+            let lang_item = self.tcx.require_lang_item(lang_items::SizedTraitLangItem, None);
+            self.require_type_meets(ty, span, code, lang_item);
+        }
+    }
 
-fn check_expr(fcx: &FnCtxt, expr: &ast::Expr)  {
-    check_expr_with_unifier(fcx, expr, NoExpectation, NoPreference, || ())
-}
+    pub fn require_type_is_sized_deferred(
+        &self,
+        ty: Ty<'tcx>,
+        span: Span,
+        code: traits::ObligationCauseCode<'tcx>,
+    ) {
+        if !ty.references_error() {
+            self.deferred_sized_obligations.borrow_mut().push((ty, span, code));
+        }
+    }
 
-fn check_expr_with_lvalue_pref(fcx: &FnCtxt, expr: &ast::Expr,
-                               lvalue_pref: LvaluePreference)  {
-    check_expr_with_unifier(fcx, expr, NoExpectation, lvalue_pref, || ())
-}
+    pub fn register_bound(
+        &self,
+        ty: Ty<'tcx>,
+        def_id: DefId,
+        cause: traits::ObligationCause<'tcx>,
+    ) {
+        if !ty.references_error() {
+            self.fulfillment_cx.borrow_mut()
+                .register_bound(self, self.param_env, ty, def_id, cause);
+        }
+    }
 
-// determine the `self` type, using fresh variables for all variables
-// declared on the impl declaration e.g., `impl<A,B> for ~[(A,B)]`
-// would return ($0, $1) where $0 and $1 are freshly instantiated type
-// variables.
-pub fn impl_self_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                              span: Span, // (potential) receiver for this impl
-                              did: ast::DefId)
-                              -> TypeAndSubsts<'tcx> {
-    let tcx = fcx.tcx();
-
-    let ity = ty::lookup_item_type(tcx, did);
-    let (n_tps, rps, raw_ty) =
-        (ity.generics.types.len(subst::TypeSpace),
-         ity.generics.regions.get_slice(subst::TypeSpace),
-         ity.ty);
-
-    let rps = fcx.inh.infcx.region_vars_for_defs(span, rps);
-    let tps = fcx.inh.infcx.next_ty_vars(n_tps);
-    let substs = subst::Substs::new_type(tps, rps);
-    let substd_ty = fcx.instantiate_type_scheme(span, &substs, &raw_ty);
-
-    TypeAndSubsts { substs: substs, ty: substd_ty }
-}
+    pub fn to_ty(&self, ast_t: &hir::Ty) -> Ty<'tcx> {
+        let t = AstConv::ast_ty_to_ty(self, ast_t);
+        self.register_wf_obligation(t, ast_t.span, traits::MiscObligation);
+        t
+    }
 
-// Only for fields! Returns <none> for methods>
-// Indifferent to privacy flags
-pub fn lookup_field_ty<'tcx>(tcx: &ty::ctxt<'tcx>,
-                             class_id: ast::DefId,
-                             items: &[ty::field_ty],
-                             fieldname: ast::Name,
-                             substs: &subst::Substs<'tcx>)
-                             -> Option<Ty<'tcx>> {
-
-    let o_field = items.iter().find(|f| f.name == fieldname);
-    o_field.map(|f| ty::lookup_field_type(tcx, class_id, f.id, substs))
-}
+    pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> {
+        let ty = self.to_ty(ast_ty);
+        debug!("to_ty_saving_user_provided_ty: ty={:?}", ty);
 
-pub fn lookup_tup_field_ty<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                 class_id: ast::DefId,
-                                 items: &[ty::field_ty],
-                                 idx: uint,
-                                 substs: &subst::Substs<'tcx>)
-                                 -> Option<Ty<'tcx>> {
+        if Self::can_contain_user_lifetime_bounds(ty) {
+            let c_ty = self.infcx.canonicalize_response(&UserType::Ty(ty));
+            debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty);
+            self.tables.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty);
+        }
 
-    let o_field = if idx < items.len() { Some(&items[idx]) } else { None };
-    o_field.map(|f| ty::lookup_field_type(tcx, class_id, f.id, substs))
-}
+        ty
+    }
 
-// Controls whether the arguments are automatically referenced. This is useful
-// for overloaded binary and unary operators.
-#[derive(Copy, PartialEq)]
-pub enum AutorefArgs {
-    Yes,
-    No,
-}
+    /// Returns the `DefId` of the constant parameter that the provided expression is a path to.
+    pub fn const_param_def_id(&self, hir_c: &hir::AnonConst) -> Option<DefId> {
+        AstConv::const_param_def_id(self, &self.tcx.hir().body(hir_c.body).value)
+    }
 
-/// Controls whether the arguments are tupled. This is used for the call
-/// operator.
-///
-/// Tupling means that all call-side arguments are packed into a tuple and
-/// passed as a single parameter. For example, if tupling is enabled, this
-/// function:
-///
-///     fn f(x: (int, int))
-///
-/// Can be called as:
-///
-///     f(1, 2);
-///
-/// Instead of:
-///
-///     f((1, 2));
-#[derive(Clone, Eq, PartialEq)]
-enum TupleArgumentsFlag {
-    DontTupleArguments,
-    TupleArguments,
-}
+    pub fn to_const(&self, ast_c: &hir::AnonConst, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> {
+        AstConv::ast_const_to_const(self, ast_c, ty)
+    }
 
-/// Invariant:
-/// If an expression has any sub-expressions that result in a type error,
-/// inspecting that expression's type with `ty::type_is_error` will return
-/// true. Likewise, if an expression is known to diverge, inspecting its
-/// type with `ty::type_is_bot` will return true (n.b.: since Rust is
-/// strict, _|_ can appear in the type of an expression that does not,
-/// itself, diverge: for example, fn() -> _|_.)
-/// Note that inspecting a type's structure *directly* may expose the fact
-/// that there are actually multiple representations for `ty_err`, so avoid
-/// that when err needs to be handled differently.
-fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
-                                        expr: &ast::Expr,
-                                        expected: Expectation<'tcx>,
-                                        lvalue_pref: LvaluePreference,
-                                        unifier: F) where
-    F: FnOnce(),
-{
-    debug!(">> typechecking: expr={} expected={}",
-           expr.repr(fcx.tcx()), expected.repr(fcx.tcx()));
-
-    // Checks a method call.
-    fn check_method_call(fcx: &FnCtxt,
-                         expr: &ast::Expr,
-                         method_name: ast::SpannedIdent,
-                         args: &[P<ast::Expr>],
-                         tps: &[P<ast::Ty>],
-                         lvalue_pref: LvaluePreference) {
-        let rcvr = &*args[0];
-        check_expr_with_lvalue_pref(fcx, &*rcvr, lvalue_pref);
-
-        // no need to check for bot/err -- callee does that
-        let expr_t = structurally_resolved_type(fcx,
-                                                expr.span,
-                                                fcx.expr_ty(&*rcvr));
-
-        let tps = tps.iter().map(|ast_ty| fcx.to_ty(&**ast_ty)).collect::<Vec<_>>();
-        let fn_ty = match method::lookup(fcx,
-                                         method_name.span,
-                                         method_name.node.name,
-                                         expr_t,
-                                         tps,
-                                         expr,
-                                         rcvr) {
-            Ok(method) => {
-                let method_ty = method.ty;
-                let method_call = MethodCall::expr(expr.id);
-                fcx.inh.method_map.borrow_mut().insert(method_call, method);
-                method_ty
-            }
-            Err(error) => {
-                method::report_error(fcx, method_name.span, expr_t, method_name.node.name, error);
-                fcx.write_error(expr.id);
-                fcx.tcx().types.err
-            }
-        };
+    // If the type given by the user has free regions, save it for later, since
+    // NLL would like to enforce those. Also pass in types that involve
+    // projections, since those can resolve to `'static` bounds (modulo #54940,
+    // which hopefully will be fixed by the time you see this comment, dear
+    // reader, although I have my doubts). Also pass in types with inference
+    // types, because they may be repeated. Other sorts of things are already
+    // sufficiently enforced with erased regions. =)
+    fn can_contain_user_lifetime_bounds<T>(t: T) -> bool
+    where
+        T: TypeFoldable<'tcx>
+    {
+        t.has_free_regions() || t.has_projections() || t.has_infer_types()
+    }
 
-        // Call the generic checker.
-        let args: Vec<_> = args[1..].iter().map(|x| x).collect();
-        let ret_ty = check_method_argument_types(fcx,
-                                                 method_name.span,
-                                                 fn_ty,
-                                                 expr,
-                                                 args.as_slice(),
-                                                 AutorefArgs::No,
-                                                 DontTupleArguments);
-
-        write_call(fcx, expr, ret_ty);
-    }
-
-    // A generic function for checking the then and else in an if
-    // or if-else.
-    fn check_then_else<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                 cond_expr: &ast::Expr,
-                                 then_blk: &ast::Block,
-                                 opt_else_expr: Option<&ast::Expr>,
-                                 id: ast::NodeId,
-                                 sp: Span,
-                                 expected: Expectation<'tcx>) {
-        check_expr_has_type(fcx, cond_expr, fcx.tcx().types.bool);
-
-        let expected = expected.adjust_for_branches(fcx);
-        check_block_with_expected(fcx, then_blk, expected);
-        let then_ty = fcx.node_ty(then_blk.id);
-
-        let branches_ty = match opt_else_expr {
-            Some(ref else_expr) => {
-                check_expr_with_expectation(fcx, &**else_expr, expected);
-                let else_ty = fcx.expr_ty(&**else_expr);
-                infer::common_supertype(fcx.infcx(),
-                                        infer::IfExpression(sp),
-                                        true,
-                                        then_ty,
-                                        else_ty)
-            }
+    pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
+        match self.tables.borrow().node_types().get(id) {
+            Some(&t) => t,
+            None if self.is_tainted_by_errors() => self.tcx.types.err,
             None => {
-                infer::common_supertype(fcx.infcx(),
-                                        infer::IfExpressionWithNoElse(sp),
-                                        false,
-                                        then_ty,
-                                        ty::mk_nil(fcx.tcx()))
+                bug!("no type for node {}: {} in fcx {}",
+                     id, self.tcx.hir().node_to_string(id),
+                     self.tag());
             }
-        };
+        }
+    }
 
-        let cond_ty = fcx.expr_ty(cond_expr);
-        let if_ty = if ty::type_is_error(cond_ty) {
-            fcx.tcx().types.err
-        } else {
-            branches_ty
-        };
+    /// Registers an obligation for checking later, during regionck, that the type `ty` must
+    /// outlive the region `r`.
+    pub fn register_wf_obligation(
+        &self,
+        ty: Ty<'tcx>,
+        span: Span,
+        code: traits::ObligationCauseCode<'tcx>,
+    ) {
+        // WF obligations never themselves fail, so no real need to give a detailed cause:
+        let cause = traits::ObligationCause::new(span, self.body_id, code);
+        self.register_predicate(
+            traits::Obligation::new(cause, self.param_env, ty::Predicate::WellFormed(ty)),
+        );
+    }
 
-        fcx.write_ty(id, if_ty);
+    /// Registers obligations that all types appearing in `substs` are well-formed.
+    pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr) {
+        for ty in substs.types() {
+            if !ty.references_error() {
+                self.register_wf_obligation(ty, expr.span, traits::MiscObligation);
+            }
+        }
     }
 
-    fn lookup_op_method<'a, 'tcx, F>(fcx: &'a FnCtxt<'a, 'tcx>,
-                                     op_ex: &ast::Expr,
-                                     lhs_ty: Ty<'tcx>,
-                                     opname: ast::Name,
-                                     trait_did: Option<ast::DefId>,
-                                     lhs: &'a ast::Expr,
-                                     rhs: Option<&P<ast::Expr>>,
-                                     unbound_method: F,
-                                     autoref_args: AutorefArgs) -> Ty<'tcx> where
-        F: FnOnce(),
+    /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
+    /// type/region parameter was instantiated (`substs`), creates and registers suitable
+    /// trait/region obligations.
+    ///
+    /// For example, if there is a function:
+    ///
+    /// ```
+    /// fn foo<'a,T:'a>(...)
+    /// ```
+    ///
+    /// and a reference:
+    ///
+    /// ```
+    /// let f = foo;
+    /// ```
+    ///
+    /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
+    /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
+    pub fn add_obligations_for_parameters(&self,
+                                          cause: traits::ObligationCause<'tcx>,
+                                          predicates: &ty::InstantiatedPredicates<'tcx>)
     {
-        let method = match trait_did {
-            Some(trait_did) => {
-                // We do eager coercions to make using operators
-                // more ergonomic:
-                //
-                // - If the input is of type &'a T (resp. &'a mut T),
-                //   then reborrow it to &'b T (resp. &'b mut T) where
-                //   'b <= 'a.  This makes things like `x == y`, where
-                //   `x` and `y` are both region pointers, work.  We
-                //   could also solve this with variance or different
-                //   traits that don't force left and right to have same
-                //   type.
-                let (adj_ty, adjustment) = match lhs_ty.sty {
-                    ty::ty_rptr(r_in, mt) => {
-                        let r_adj = fcx.infcx().next_region_var(infer::Autoref(lhs.span));
-                        fcx.mk_subr(infer::Reborrow(lhs.span), r_adj, *r_in);
-                        let adjusted_ty = ty::mk_rptr(fcx.tcx(), fcx.tcx().mk_region(r_adj), mt);
-                        let autoptr = ty::AutoPtr(r_adj, mt.mutbl, None);
-                        let adjustment = ty::AutoDerefRef { autoderefs: 1, autoref: Some(autoptr) };
-                        (adjusted_ty, adjustment)
-                    }
-                    _ => {
-                        (lhs_ty, ty::AutoDerefRef { autoderefs: 0, autoref: None })
-                    }
-                };
+        assert!(!predicates.has_escaping_bound_vars());
 
-                debug!("adjusted_ty={} adjustment={:?}",
-                       adj_ty.repr(fcx.tcx()),
-                       adjustment);
+        debug!("add_obligations_for_parameters(predicates={:?})",
+               predicates);
 
-                method::lookup_in_trait_adjusted(fcx, op_ex.span, Some(lhs), opname,
-                                                 trait_did, adjustment, adj_ty, None)
-            }
-            None => None
-        };
-        let args = match rhs {
-            Some(rhs) => vec![rhs],
-            None => vec![]
-        };
-        match method {
-            Some(method) => {
-                let method_ty = method.ty;
-                // HACK(eddyb) Fully qualified path to work around a resolve bug.
-                let method_call = ::middle::ty::MethodCall::expr(op_ex.id);
-                fcx.inh.method_map.borrow_mut().insert(method_call, method);
-                match check_method_argument_types(fcx,
-                                            op_ex.span,
-                                            method_ty,
-                                            op_ex,
-                                            args.as_slice(),
-                                            autoref_args,
-                                            DontTupleArguments) {
-                    ty::FnConverging(result_type) => result_type,
-                    ty::FnDiverging => fcx.tcx().types.err
-                }
-            }
-            None => {
-                unbound_method();
-                // Check the args anyway
-                // so we get all the error messages
-                let expected_ty = fcx.tcx().types.err;
-                check_method_argument_types(fcx,
-                                            op_ex.span,
-                                            expected_ty,
-                                            op_ex,
-                                            args.as_slice(),
-                                            autoref_args,
-                                            DontTupleArguments);
-                fcx.tcx().types.err
-            }
+        for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
+            self.register_predicate(obligation);
         }
     }
 
-    // could be either an expr_binop or an expr_assign_binop
-    fn check_binop(fcx: &FnCtxt,
-                   expr: &ast::Expr,
-                   op: ast::BinOp,
-                   lhs: &ast::Expr,
-                   rhs: &P<ast::Expr>,
-                   is_binop_assignment: IsBinopAssignment) {
-        let tcx = fcx.ccx.tcx;
+    // FIXME(arielb1): use this instead of field.ty everywhere
+    // Only for fields! Returns <none> for methods>
+    // Indifferent to privacy flags
+    pub fn field_ty(
+        &self,
+        span: Span,
+        field: &'tcx ty::FieldDef,
+        substs: SubstsRef<'tcx>,
+    ) -> Ty<'tcx> {
+        self.normalize_associated_types_in(span, &field.ty(self.tcx, substs))
+    }
 
-        let lvalue_pref = match is_binop_assignment {
-            BinopAssignment => PreferMutLvalue,
-            SimpleBinop => NoPreference
-        };
-        check_expr_with_lvalue_pref(fcx, &*lhs, lvalue_pref);
-
-        // Callee does bot / err checking
-        let lhs_t = structurally_resolved_type(fcx, lhs.span,
-                                               fcx.expr_ty(&*lhs));
-
-        if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op) {
-            // Shift is a special case: rhs must be uint, no matter what lhs is
-            check_expr(fcx, &**rhs);
-            let rhs_ty = fcx.expr_ty(&**rhs);
-            let rhs_ty = fcx.infcx().resolve_type_vars_if_possible(&rhs_ty);
-            if ty::type_is_integral(rhs_ty) {
-                fcx.write_ty(expr.id, lhs_t);
-            } else {
-                fcx.type_error_message(
-                    expr.span,
-                    |actual| {
-                        format!(
-                            "right-hand-side of a shift operation must have integral type, \
-                             not `{}`",
-                            actual)
-                    },
-                    rhs_ty,
-                    None);
-                fcx.write_ty(expr.id, fcx.tcx().types.err);
-            }
-            return;
+    fn check_casts(&self) {
+        let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
+        for cast in deferred_cast_checks.drain(..) {
+            cast.check(self);
         }
+    }
 
-        if ty::is_binopable(tcx, lhs_t, op) {
-            let tvar = fcx.infcx().next_ty_var();
-            demand::suptype(fcx, expr.span, tvar, lhs_t);
-            check_expr_has_type(fcx, &**rhs, tvar);
-
-            let result_t = match op {
-                ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGe |
-                ast::BiGt => {
-                    if ty::type_is_simd(tcx, lhs_t) {
-                        if ty::type_is_fp(ty::simd_type(tcx, lhs_t)) {
-                            fcx.type_error_message(expr.span,
-                                |actual| {
-                                    format!("binary comparison \
-                                             operation `{}` not \
-                                             supported for floating \
-                                             point SIMD vector `{}`",
-                                            ast_util::binop_to_string(op),
-                                            actual)
-                                },
-                                lhs_t,
-                                None
-                            );
-                            fcx.tcx().types.err
-                        } else {
-                            lhs_t
-                        }
+    fn resolve_generator_interiors(&self, def_id: DefId) {
+        let mut generators = self.deferred_generator_interiors.borrow_mut();
+        for (body_id, interior, kind) in generators.drain(..) {
+            self.select_obligations_where_possible(false, |_| {});
+            generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
+        }
+    }
+
+    // Tries to apply a fallback to `ty` if it is an unsolved variable.
+    //
+    // - Unconstrained ints are replaced with `i32`.
+    //
+    // - Unconstrained floats are replaced with with `f64`.
+    //
+    // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
+    //   is enabled. Otherwise, they are replaced with `()`.
+    //
+    // Fallback becomes very dubious if we have encountered type-checking errors.
+    // In that case, fallback to Error.
+    // The return value indicates whether fallback has occurred.
+    fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
+        use rustc::ty::error::UnconstrainedNumeric::Neither;
+        use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat};
+
+        assert!(ty.is_ty_infer());
+        let fallback = match self.type_is_unconstrained_numeric(ty) {
+            _ if self.is_tainted_by_errors() => self.tcx().types.err,
+            UnconstrainedInt => self.tcx.types.i32,
+            UnconstrainedFloat => self.tcx.types.f64,
+            Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(),
+            Neither => {
+                // This type variable was created from the instantiation of an opaque
+                // type. The fact that we're attempting to perform fallback for it
+                // means that the function neither constrained it to a concrete
+                // type, nor to the opaque type itself.
+                //
+                // For example, in this code:
+                //
+                //```
+                // type MyType = impl Copy;
+                // fn defining_use() -> MyType { true }
+                // fn other_use() -> MyType { defining_use() }
+                // ```
+                //
+                // `defining_use` will constrain the instantiated inference
+                // variable to `bool`, while `other_use` will constrain
+                // the instantiated inference variable to `MyType`.
+                //
+                // When we process opaque types during writeback, we
+                // will handle cases like `other_use`, and not count
+                // them as defining usages
+                //
+                // However, we also need to handle cases like this:
+                //
+                // ```rust
+                // pub type Foo = impl Copy;
+                // fn produce() -> Option<Foo> {
+                //     None
+                //  }
+                //  ```
+                //
+                // In the above snippet, the inference varaible created by
+                // instantiating `Option<Foo>` will be completely unconstrained.
+                // We treat this as a non-defining use by making the inference
+                // variable fall back to the opaque type itself.
+                if let FallbackMode::All = mode {
+                    if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) {
+                        debug!("fallback_if_possible: falling back opaque type var {:?} to {:?}",
+                               ty, opaque_ty);
+                        *opaque_ty
                     } else {
-                        fcx.tcx().types.bool
+                        return false;
                     }
-                },
-                _ => lhs_t,
-            };
+                } else {
+                    return false;
+                }
+            },
+        };
+        debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
+        self.demand_eqtype(syntax_pos::DUMMY_SP, ty, fallback);
+        true
+    }
 
-            fcx.write_ty(expr.id, result_t);
-            return;
+    fn select_all_obligations_or_error(&self) {
+        debug!("select_all_obligations_or_error");
+        if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) {
+            self.report_fulfillment_errors(&errors, self.inh.body_id, false);
         }
+    }
 
-        if op == ast::BiOr || op == ast::BiAnd {
-            // This is an error; one of the operands must have the wrong
-            // type
-            fcx.write_error(expr.id);
-            fcx.write_error(rhs.id);
-            fcx.type_error_message(expr.span,
-                                   |actual| {
-                    format!("binary operation `{}` cannot be applied \
-                             to type `{}`",
-                            ast_util::binop_to_string(op),
-                            actual)
-                },
-                lhs_t,
-                None)
+    /// Select as many obligations as we can at present.
+    fn select_obligations_where_possible(
+        &self,
+        fallback_has_occurred: bool,
+        mutate_fullfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
+    ) {
+        let result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
+        if let Err(mut errors) = result {
+            mutate_fullfillment_errors(&mut errors);
+            self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred);
         }
+    }
 
-        // Check for overloaded operators if not an assignment.
-        let result_t = if is_binop_assignment == SimpleBinop {
-            check_user_binop(fcx, expr, lhs, lhs_t, op, rhs)
-        } else {
-            fcx.type_error_message(expr.span,
-                                   |actual| {
-                                        format!("binary assignment \
-                                                 operation `{}=` \
-                                                 cannot be applied to \
-                                                 type `{}`",
-                                                ast_util::binop_to_string(op),
-                                                actual)
-                                   },
-                                   lhs_t,
-                                   None);
-            check_expr(fcx, &**rhs);
-            fcx.tcx().types.err
-        };
+    /// For the overloaded place expressions (`*x`, `x[3]`), the trait
+    /// returns a type of `&T`, but the actual type we assign to the
+    /// *expression* is `T`. So this function just peels off the return
+    /// type by one layer to yield `T`.
+    fn make_overloaded_place_return_type(&self,
+                                          method: MethodCallee<'tcx>)
+                                          -> ty::TypeAndMut<'tcx>
+    {
+        // extract method return type, which will be &T;
+        let ret_ty = method.sig.output();
+
+        // method returns &T, but the type as visible to user is T, so deref
+        ret_ty.builtin_deref(true).unwrap()
+    }
+
+    fn lookup_indexing(
+        &self,
+        expr: &hir::Expr,
+        base_expr: &'tcx hir::Expr,
+        base_ty: Ty<'tcx>,
+        idx_ty: Ty<'tcx>,
+        needs: Needs,
+    ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
+        // FIXME(#18741) -- this is almost but not quite the same as the
+        // autoderef that normal method probing does. They could likely be
+        // consolidated.
+
+        let mut autoderef = self.autoderef(base_expr.span, base_ty);
+        let mut result = None;
+        while result.is_none() && autoderef.next().is_some() {
+            result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty);
+        }
+        autoderef.finalize(self);
+        result
+    }
 
-        fcx.write_ty(expr.id, result_t);
-        if ty::type_is_error(result_t) {
-            fcx.write_ty(rhs.id, result_t);
-        }
-    }
-
-    fn check_user_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                  ex: &ast::Expr,
-                                  lhs_expr: &ast::Expr,
-                                  lhs_resolved_t: Ty<'tcx>,
-                                  op: ast::BinOp,
-                                  rhs: &P<ast::Expr>) -> Ty<'tcx> {
-        let tcx = fcx.ccx.tcx;
-        let lang = &tcx.lang_items;
-        let (name, trait_did) = match op {
-            ast::BiAdd => ("add", lang.add_trait()),
-            ast::BiSub => ("sub", lang.sub_trait()),
-            ast::BiMul => ("mul", lang.mul_trait()),
-            ast::BiDiv => ("div", lang.div_trait()),
-            ast::BiRem => ("rem", lang.rem_trait()),
-            ast::BiBitXor => ("bitxor", lang.bitxor_trait()),
-            ast::BiBitAnd => ("bitand", lang.bitand_trait()),
-            ast::BiBitOr => ("bitor", lang.bitor_trait()),
-            ast::BiShl => ("shl", lang.shl_trait()),
-            ast::BiShr => ("shr", lang.shr_trait()),
-            ast::BiLt => ("lt", lang.ord_trait()),
-            ast::BiLe => ("le", lang.ord_trait()),
-            ast::BiGe => ("ge", lang.ord_trait()),
-            ast::BiGt => ("gt", lang.ord_trait()),
-            ast::BiEq => ("eq", lang.eq_trait()),
-            ast::BiNe => ("ne", lang.eq_trait()),
-            ast::BiAnd | ast::BiOr => {
-                check_expr(fcx, &**rhs);
-                return tcx.types.err;
-            }
-        };
-        lookup_op_method(fcx, ex, lhs_resolved_t, token::intern(name),
-                         trait_did, lhs_expr, Some(rhs), || {
-            fcx.type_error_message(ex.span, |actual| {
-                format!("binary operation `{}` cannot be applied to type `{}`",
-                        ast_util::binop_to_string(op),
-                        actual)
-            }, lhs_resolved_t, None)
-        }, if ast_util::is_by_value_binop(op) { AutorefArgs::No } else { AutorefArgs::Yes })
-    }
-
-    fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                 op_str: &str,
-                                 mname: &str,
-                                 trait_did: Option<ast::DefId>,
-                                 ex: &ast::Expr,
-                                 rhs_expr: &ast::Expr,
-                                 rhs_t: Ty<'tcx>,
-                                 op: ast::UnOp) -> Ty<'tcx> {
-       lookup_op_method(fcx, ex, rhs_t, token::intern(mname),
-                        trait_did, rhs_expr, None, || {
-            fcx.type_error_message(ex.span, |actual| {
-                format!("cannot apply unary operator `{}` to type `{}`",
-                        op_str, actual)
-            }, rhs_t, None);
-        }, if ast_util::is_by_value_unop(op) { AutorefArgs::No } else { AutorefArgs::Yes })
-    }
-
-    // Check field access expressions
-    fn check_field(fcx: &FnCtxt,
-                   expr: &ast::Expr,
-                   lvalue_pref: LvaluePreference,
-                   base: &ast::Expr,
-                   field: &ast::SpannedIdent) {
-        let tcx = fcx.ccx.tcx;
-        check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
-        let expr_t = structurally_resolved_type(fcx, expr.span,
-                                                fcx.expr_ty(base));
-        // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop.
-        let (_, autoderefs, field_ty) =
-            autoderef(fcx, expr.span, expr_t, Some(base), lvalue_pref, |base_t, _| {
-                match base_t.sty {
-                    ty::ty_struct(base_id, substs) => {
-                        debug!("struct named {}", ppaux::ty_to_string(tcx, base_t));
-                        let fields = ty::lookup_struct_fields(tcx, base_id);
-                        lookup_field_ty(tcx, base_id, &fields[],
-                                        field.node.name, &(*substs))
-                    }
-                    _ => None
+    /// To type-check `base_expr[index_expr]`, we progressively autoderef
+    /// (and otherwise adjust) `base_expr`, looking for a type which either
+    /// supports builtin indexing or overloaded indexing.
+    /// This loop implements one step in that search; the autoderef loop
+    /// is implemented by `lookup_indexing`.
+    fn try_index_step(
+        &self,
+        expr: &hir::Expr,
+        base_expr: &hir::Expr,
+        autoderef: &Autoderef<'a, 'tcx>,
+        needs: Needs,
+        index_ty: Ty<'tcx>,
+    ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
+        let adjusted_ty = autoderef.unambiguous_final_ty(self);
+        debug!("try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
+                               index_ty={:?})",
+               expr,
+               base_expr,
+               adjusted_ty,
+               index_ty);
+
+        for &unsize in &[false, true] {
+            let mut self_ty = adjusted_ty;
+            if unsize {
+                // We only unsize arrays here.
+                if let ty::Array(element_ty, _) = adjusted_ty.kind {
+                    self_ty = self.tcx.mk_slice(element_ty);
+                } else {
+                    continue;
                 }
-            });
-        match field_ty {
-            Some(field_ty) => {
-                fcx.write_ty(expr.id, field_ty);
-                fcx.write_autoderef_adjustment(base.id, base.span, autoderefs);
-                return;
             }
-            None => {}
-        }
 
-        if method::exists(fcx, field.span, field.node.name, expr_t, expr.id) {
-            fcx.type_error_message(
-                field.span,
-                |actual| {
-                    format!("attempted to take value of method `{}` on type \
-                            `{}`", token::get_ident(field.node), actual)
-                },
-                expr_t, None);
-
-            tcx.sess.span_help(field.span,
-                               "maybe a `()` to call it is missing? \
-                               If not, try an anonymous function");
-        } else {
-            fcx.type_error_message(
-                expr.span,
-                |actual| {
-                    format!("attempted access of field `{}` on \
-                            type `{}`, but no field with that \
-                            name was found",
-                            token::get_ident(field.node),
-                            actual)
-                },
-                expr_t, None);
-        }
-
-        fcx.write_error(expr.id);
-    }
-
-    // Check tuple index expressions
-    fn check_tup_field(fcx: &FnCtxt,
-                       expr: &ast::Expr,
-                       lvalue_pref: LvaluePreference,
-                       base: &ast::Expr,
-                       idx: codemap::Spanned<uint>) {
-        let tcx = fcx.ccx.tcx;
-        check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
-        let expr_t = structurally_resolved_type(fcx, expr.span,
-                                                fcx.expr_ty(base));
-        let mut tuple_like = false;
-        // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop.
-        let (_, autoderefs, field_ty) =
-            autoderef(fcx, expr.span, expr_t, Some(base), lvalue_pref, |base_t, _| {
-                match base_t.sty {
-                    ty::ty_struct(base_id, substs) => {
-                        tuple_like = ty::is_tuple_struct(tcx, base_id);
-                        if tuple_like {
-                            debug!("tuple struct named {}", ppaux::ty_to_string(tcx, base_t));
-                            let fields = ty::lookup_struct_fields(tcx, base_id);
-                            lookup_tup_field_ty(tcx, base_id, &fields[],
-                                                idx.node, &(*substs))
-                        } else {
-                            None
-                        }
-                    }
-                    ty::ty_tup(ref v) => {
-                        tuple_like = true;
-                        if idx.node < v.len() { Some(v[idx.node]) } else { None }
-                    }
-                    _ => None
-                }
+            // If some lookup succeeds, write callee into table and extract index/element
+            // type from the method signature.
+            // If some lookup succeeded, install method in table
+            let input_ty = self.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::AutoDeref,
+                span: base_expr.span,
             });
-        match field_ty {
-            Some(field_ty) => {
-                fcx.write_ty(expr.id, field_ty);
-                fcx.write_autoderef_adjustment(base.id, base.span, autoderefs);
-                return;
-            }
-            None => {}
-        }
-        fcx.type_error_message(
-            expr.span,
-            |actual| {
-                if tuple_like {
-                    format!("attempted out-of-bounds tuple index `{}` on \
-                                    type `{}`",
-                                   idx.node,
-                                   actual)
-                } else {
-                    format!("attempted tuple index `{}` on type `{}`, but the \
-                                     type was not a tuple or tuple struct",
-                                    idx.node,
-                                    actual)
-                }
-            },
-            expr_t, None);
-
-        fcx.write_error(expr.id);
-    }
-
-    fn check_struct_or_variant_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                struct_ty: Ty<'tcx>,
-                                                span: Span,
-                                                class_id: ast::DefId,
-                                                node_id: ast::NodeId,
-                                                substitutions: &'tcx subst::Substs<'tcx>,
-                                                field_types: &[ty::field_ty],
-                                                ast_fields: &[ast::Field],
-                                                check_completeness: bool,
-                                                enum_id_opt: Option<ast::DefId>)  {
-        let tcx = fcx.ccx.tcx;
-
-        let mut class_field_map = FnvHashMap::new();
-        let mut fields_found = 0;
-        for field in field_types.iter() {
-            class_field_map.insert(field.name, (field.id, false));
-        }
-
-        let mut error_happened = false;
-
-        // Typecheck each field.
-        for field in ast_fields.iter() {
-            let mut expected_field_type = tcx.types.err;
-
-            let pair = class_field_map.get(&field.ident.node.name).map(|x| *x);
-            match pair {
-                None => {
-                    fcx.type_error_message(
-                        field.ident.span,
-                        |actual| match enum_id_opt {
-                            Some(enum_id) => {
-                                let variant_type = ty::enum_variant_with_id(tcx,
-                                                                            enum_id,
-                                                                            class_id);
-                                format!("struct variant `{}::{}` has no field named `{}`",
-                                        actual, variant_type.name.as_str(),
-                                        token::get_ident(field.ident.node))
-                            }
-                            None => {
-                                format!("structure `{}` has no field named `{}`",
-                                        actual,
-                                        token::get_ident(field.ident.node))
-                            }
-                        },
-                        struct_ty,
-                        None);
-                    error_happened = true;
-                }
-                Some((_, true)) => {
-                    span_err!(fcx.tcx().sess, field.ident.span, E0062,
-                        "field `{}` specified more than once",
-                        token::get_ident(field.ident.node));
-                    error_happened = true;
+            let method = self.try_overloaded_place_op(
+                expr.span, self_ty, &[input_ty], needs, PlaceOp::Index);
+
+            let result = method.map(|ok| {
+                debug!("try_index_step: success, using overloaded indexing");
+                let method = self.register_infer_ok_obligations(ok);
+
+                let mut adjustments = autoderef.adjust_steps(self, needs);
+                if let ty::Ref(region, _, r_mutbl) = method.sig.inputs()[0].kind {
+                    let mutbl = match r_mutbl {
+                        hir::Mutability::Immutable => AutoBorrowMutability::Immutable,
+                        hir::Mutability::Mutable => AutoBorrowMutability::Mutable {
+                            // Indexing can be desugared to a method call,
+                            // so maybe we could use two-phase here.
+                            // See the documentation of AllowTwoPhase for why that's
+                            // not the case today.
+                            allow_two_phase_borrow: AllowTwoPhase::No,
+                        }
+                    };
+                    adjustments.push(Adjustment {
+                        kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
+                        target: self.tcx.mk_ref(region, ty::TypeAndMut {
+                            mutbl: r_mutbl,
+                            ty: adjusted_ty
+                        })
+                    });
                 }
-                Some((field_id, false)) => {
-                    expected_field_type =
-                        ty::lookup_field_type(
-                            tcx, class_id, field_id, substitutions);
-                    expected_field_type =
-                        fcx.normalize_associated_types_in(
-                            field.span, &expected_field_type);
-                    class_field_map.insert(
-                        field.ident.node.name, (field_id, true));
-                    fields_found += 1;
+                if unsize {
+                    adjustments.push(Adjustment {
+                        kind: Adjust::Pointer(PointerCast::Unsize),
+                        target: method.sig.inputs()[0]
+                    });
                 }
-            }
+                self.apply_adjustments(base_expr, adjustments);
 
-            // Make sure to give a type to the field even if there's
-            // an error, so we can continue typechecking
-            check_expr_coercable_to_type(fcx, &*field.expr, expected_field_type);
+                self.write_method_call(expr.hir_id, method);
+                (input_ty, self.make_overloaded_place_return_type(method).ty)
+            });
+            if result.is_some() {
+                return result;
+            }
         }
 
-        if error_happened {
-            fcx.write_error(node_id);
-        }
+        None
+    }
 
-        if check_completeness && !error_happened {
-            // Make sure the programmer specified all the fields.
-            assert!(fields_found <= field_types.len());
-            if fields_found < field_types.len() {
-                let mut missing_fields = Vec::new();
-                for class_field in field_types.iter() {
-                    let name = class_field.name;
-                    let (_, seen) = class_field_map[name];
-                    if !seen {
-                        missing_fields.push(
-                            format!("`{}`", token::get_name(name).get()))
-                    }
-                }
+    fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option<DefId>, ast::Ident) {
+        let (tr, name) = match (op, is_mut) {
+            (PlaceOp::Deref, false) => (self.tcx.lang_items().deref_trait(), sym::deref),
+            (PlaceOp::Deref, true) => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
+            (PlaceOp::Index, false) => (self.tcx.lang_items().index_trait(), sym::index),
+            (PlaceOp::Index, true) => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
+        };
+        (tr, ast::Ident::with_dummy_span(name))
+    }
 
-                span_err!(tcx.sess, span, E0063,
-                    "missing field{}: {}",
-                    if missing_fields.len() == 1 {""} else {"s"},
-                    missing_fields.connect(", "));
-             }
-        }
-
-        if !error_happened {
-            fcx.write_ty(node_id, ty::mk_struct(fcx.ccx.tcx,
-                                class_id, substitutions));
-        }
-    }
-
-    fn check_struct_constructor(fcx: &FnCtxt,
-                                id: ast::NodeId,
-                                span: codemap::Span,
-                                class_id: ast::DefId,
-                                fields: &[ast::Field],
-                                base_expr: Option<&ast::Expr>) {
-        let tcx = fcx.ccx.tcx;
-
-        // Generate the struct type.
-        let TypeAndSubsts {
-            ty: mut struct_type,
-            substs: struct_substs
-        } = fcx.instantiate_type(span, class_id);
-
-        // Look up and check the fields.
-        let class_fields = ty::lookup_struct_fields(tcx, class_id);
-        check_struct_or_variant_fields(fcx,
-                                       struct_type,
-                                       span,
-                                       class_id,
-                                       id,
-                                       fcx.ccx.tcx.mk_substs(struct_substs),
-                                       &class_fields[],
-                                       fields,
-                                       base_expr.is_none(),
-                                       None);
-        if ty::type_is_error(fcx.node_ty(id)) {
-            struct_type = tcx.types.err;
-        }
-
-        // Check the base expression if necessary.
-        match base_expr {
-            None => {}
-            Some(base_expr) => {
-                check_expr_has_type(fcx, &*base_expr, struct_type);
+    fn try_overloaded_place_op(&self,
+                                span: Span,
+                                base_ty: Ty<'tcx>,
+                                arg_tys: &[Ty<'tcx>],
+                                needs: Needs,
+                                op: PlaceOp)
+                                -> Option<InferOk<'tcx, MethodCallee<'tcx>>>
+    {
+        debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})",
+               span,
+               base_ty,
+               needs,
+               op);
+
+        // Try Mut first, if needed.
+        let (mut_tr, mut_op) = self.resolve_place_op(op, true);
+        let method = match (needs, mut_tr) {
+            (Needs::MutPlace, Some(trait_did)) => {
+                self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys))
             }
-        }
+            _ => None,
+        };
 
-        // Write in the resulting type.
-        fcx.write_ty(id, struct_type);
-    }
-
-    fn check_struct_enum_variant(fcx: &FnCtxt,
-                                 id: ast::NodeId,
-                                 span: codemap::Span,
-                                 enum_id: ast::DefId,
-                                 variant_id: ast::DefId,
-                                 fields: &[ast::Field]) {
-        let tcx = fcx.ccx.tcx;
-
-        // Look up the number of type parameters and the raw type, and
-        // determine whether the enum is region-parameterized.
-        let TypeAndSubsts {
-            ty: enum_type,
-            substs: substitutions
-        } = fcx.instantiate_type(span, enum_id);
-
-        // Look up and check the enum variant fields.
-        let variant_fields = ty::lookup_struct_fields(tcx, variant_id);
-        check_struct_or_variant_fields(fcx,
-                                       enum_type,
-                                       span,
-                                       variant_id,
-                                       id,
-                                       fcx.ccx.tcx.mk_substs(substitutions),
-                                       &variant_fields[],
-                                       fields,
-                                       true,
-                                       Some(enum_id));
-        fcx.write_ty(id, enum_type);
-    }
-
-    fn check_struct_fields_on_error(fcx: &FnCtxt,
-                                    id: ast::NodeId,
-                                    fields: &[ast::Field],
-                                    base_expr: &Option<P<ast::Expr>>) {
-        // Make sure to still write the types
-        // otherwise we might ICE
-        fcx.write_error(id);
-        for field in fields.iter() {
-            check_expr(fcx, &*field.expr);
-        }
-        match *base_expr {
-            Some(ref base) => check_expr(fcx, &**base),
-            None => {}
-        }
-    }
-
-    type ExprCheckerWithTy = fn(&FnCtxt, &ast::Expr, Ty);
-
-    let tcx = fcx.ccx.tcx;
-    let id = expr.id;
-    match expr.node {
-      ast::ExprBox(ref opt_place, ref subexpr) => {
-          opt_place.as_ref().map(|place|check_expr(fcx, &**place));
-          check_expr(fcx, &**subexpr);
-
-          let mut checked = false;
-          opt_place.as_ref().map(|place| match place.node {
-              ast::ExprPath(ref path) => {
-                  // FIXME(pcwalton): For now we hardcode the two permissible
-                  // places: the exchange heap and the managed heap.
-                  let definition = lookup_def(fcx, path.span, place.id);
-                  let def_id = definition.def_id();
-                  let referent_ty = fcx.expr_ty(&**subexpr);
-                  if tcx.lang_items.exchange_heap() == Some(def_id) {
-                      fcx.write_ty(id, ty::mk_uniq(tcx, referent_ty));
-                      checked = true
-                  }
-              }
-              _ => {}
-          });
-
-          if !checked {
-              span_err!(tcx.sess, expr.span, E0066,
-                  "only the managed heap and exchange heap are currently supported");
-              fcx.write_ty(id, tcx.types.err);
-          }
-      }
-
-      ast::ExprLit(ref lit) => {
-        let typ = check_lit(fcx, &**lit, expected);
-        fcx.write_ty(id, typ);
-      }
-      ast::ExprBinary(op, ref lhs, ref rhs) => {
-        check_binop(fcx, expr, op, &**lhs, rhs, SimpleBinop);
-
-        let lhs_ty = fcx.expr_ty(&**lhs);
-        let rhs_ty = fcx.expr_ty(&**rhs);
-        if ty::type_is_error(lhs_ty) ||
-            ty::type_is_error(rhs_ty) {
-            fcx.write_error(id);
-        }
-      }
-      ast::ExprAssignOp(op, ref lhs, ref rhs) => {
-        check_binop(fcx, expr, op, &**lhs, rhs, BinopAssignment);
-
-        let lhs_t = fcx.expr_ty(&**lhs);
-        let result_t = fcx.expr_ty(expr);
-        demand::suptype(fcx, expr.span, result_t, lhs_t);
-
-        let tcx = fcx.tcx();
-        if !ty::expr_is_lval(tcx, &**lhs) {
-            span_err!(tcx.sess, lhs.span, E0067, "illegal left-hand side expression");
-        }
-
-        fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized);
-
-        // Overwrite result of check_binop...this preserves existing behavior
-        // but seems quite dubious with regard to user-defined methods
-        // and so forth. - Niko
-        if !ty::type_is_error(result_t) {
-            fcx.write_nil(expr.id);
-        }
-      }
-      ast::ExprUnary(unop, ref oprnd) => {
-        let expected_inner = expected.map(fcx, |ty| {
-            match unop {
-                ast::UnUniq => match ty.sty {
-                    ty::ty_uniq(ty) => {
-                        Expectation::rvalue_hint(ty)
-                    }
-                    _ => {
-                        NoExpectation
-                    }
-                },
-                ast::UnNot | ast::UnNeg => {
-                    expected
-                }
-                ast::UnDeref => {
-                    NoExpectation
-                }
+        // Otherwise, fall back to the immutable version.
+        let (imm_tr, imm_op) = self.resolve_place_op(op, false);
+        let method = match (method, imm_tr) {
+            (None, Some(trait_did)) => {
+                self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys))
             }
-        });
-        let lvalue_pref = match unop {
-            ast::UnDeref => lvalue_pref,
-            _ => NoPreference
+            (method, _) => method,
         };
-        check_expr_with_expectation_and_lvalue_pref(
-            fcx, &**oprnd, expected_inner, lvalue_pref);
-        let mut oprnd_t = fcx.expr_ty(&**oprnd);
-
-        if !ty::type_is_error(oprnd_t) {
-            match unop {
-                ast::UnUniq => {
-                    oprnd_t = ty::mk_uniq(tcx, oprnd_t);
-                }
-                ast::UnDeref => {
-                    oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t);
-                    oprnd_t = match ty::deref(oprnd_t, true) {
-                        Some(mt) => mt.ty,
-                        None => match try_overloaded_deref(fcx, expr.span,
-                                                           Some(MethodCall::expr(expr.id)),
-                                                           Some(&**oprnd), oprnd_t, lvalue_pref) {
-                            Some(mt) => mt.ty,
-                            None => {
-                                let is_newtype = match oprnd_t.sty {
-                                    ty::ty_struct(did, substs) => {
-                                        let fields = ty::struct_fields(fcx.tcx(), did, substs);
-                                        fields.len() == 1
-                                        && fields[0].name ==
-                                        token::special_idents::unnamed_field.name
-                                    }
-                                    _ => false
-                                };
-                                if is_newtype {
-                                    // This is an obsolete struct deref
-                                    span_err!(tcx.sess, expr.span, E0068,
-                                        "single-field tuple-structs can \
-                                         no longer be dereferenced");
-                                } else {
-                                    fcx.type_error_message(expr.span, |actual| {
-                                        format!("type `{}` cannot be \
-                                                dereferenced", actual)
-                                    }, oprnd_t, None);
-                                }
-                                tcx.types.err
-                            }
-                        }
-                    };
-                }
-                ast::UnNot => {
-                    oprnd_t = structurally_resolved_type(fcx, oprnd.span,
-                                                         oprnd_t);
-                    if !(ty::type_is_integral(oprnd_t) ||
-                         oprnd_t.sty == ty::ty_bool) {
-                        oprnd_t = check_user_unop(fcx, "!", "not",
-                                                  tcx.lang_items.not_trait(),
-                                                  expr, &**oprnd, oprnd_t, unop);
-                    }
-                }
-                ast::UnNeg => {
-                    oprnd_t = structurally_resolved_type(fcx, oprnd.span,
-                                                         oprnd_t);
-                    if !(ty::type_is_integral(oprnd_t) ||
-                         ty::type_is_fp(oprnd_t)) {
-                        oprnd_t = check_user_unop(fcx, "-", "neg",
-                                                  tcx.lang_items.neg_trait(),
-                                                  expr, &**oprnd, oprnd_t, unop);
-                    }
-                }
-            }
-        }
-        fcx.write_ty(id, oprnd_t);
-      }
-      ast::ExprAddrOf(mutbl, ref oprnd) => {
-        let expected = expected.only_has_type();
-        let hint = expected.map(fcx, |ty| {
-            match ty.sty {
-                ty::ty_rptr(_, ref mt) | ty::ty_ptr(ref mt) => {
-                    if ty::expr_is_lval(fcx.tcx(), &**oprnd) {
-                        // Lvalues may legitimately have unsized types.
-                        // For example, dereferences of a fat pointer and
-                        // the last field of a struct can be unsized.
-                        ExpectHasType(mt.ty)
-                    } else {
-                        Expectation::rvalue_hint(mt.ty)
-                    }
-                }
-                _ => NoExpectation
+
+        method
+    }
+
+    fn check_method_argument_types(
+        &self,
+        sp: Span,
+        expr: &'tcx hir::Expr,
+        method: Result<MethodCallee<'tcx>, ()>,
+        args_no_rcvr: &'tcx [hir::Expr],
+        tuple_arguments: TupleArgumentsFlag,
+        expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
+
+        let has_error = match method {
+            Ok(method) => {
+                method.substs.references_error() || method.sig.references_error()
             }
-        });
-        let lvalue_pref = match mutbl {
-            ast::MutMutable => PreferMutLvalue,
-            ast::MutImmutable => NoPreference
+            Err(_) => true
         };
-        check_expr_with_expectation_and_lvalue_pref(fcx,
-                                                    &**oprnd,
-                                                    hint,
-                                                    lvalue_pref);
-
-        let tm = ty::mt { ty: fcx.expr_ty(&**oprnd), mutbl: mutbl };
-        let oprnd_t = if ty::type_is_error(tm.ty) {
-            tcx.types.err
+        if has_error {
+            let err_inputs = self.err_args(args_no_rcvr.len());
+
+            let err_inputs = match tuple_arguments {
+                DontTupleArguments => err_inputs,
+                TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])],
+            };
+
+            self.check_argument_types(
+                sp,
+                expr,
+                &err_inputs[..],
+                &[],
+                args_no_rcvr,
+                false,
+                tuple_arguments,
+                None,
+            );
+            return self.tcx.types.err;
+        }
+
+        let method = method.unwrap();
+        // HACK(eddyb) ignore self in the definition (see above).
+        let expected_arg_tys = self.expected_inputs_for_expected_output(
+            sp,
+            expected,
+            method.sig.output(),
+            &method.sig.inputs()[1..]
+        );
+        self.check_argument_types(
+            sp,
+            expr,
+            &method.sig.inputs()[1..],
+            &expected_arg_tys[..],
+            args_no_rcvr,
+            method.sig.c_variadic,
+            tuple_arguments,
+            self.tcx.hir().span_if_local(method.def_id),
+        );
+        method.sig.output()
+    }
+
+    fn self_type_matches_expected_vid(
+        &self,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        expected_vid: ty::TyVid,
+    ) -> bool {
+        let self_ty = self.shallow_resolve(trait_ref.self_ty());
+        debug!(
+            "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
+            trait_ref, self_ty, expected_vid
+        );
+        match self_ty.kind {
+            ty::Infer(ty::TyVar(found_vid)) => {
+                // FIXME: consider using `sub_root_var` here so we
+                // can see through subtyping.
+                let found_vid = self.root_var(found_vid);
+                debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
+                expected_vid == found_vid
+            }
+            _ => false
+        }
+    }
+
+    fn obligations_for_self_ty<'b>(
+        &'b self,
+        self_ty: ty::TyVid,
+    ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
+                 + Captures<'tcx>
+                 + 'b {
+        // FIXME: consider using `sub_root_var` here so we
+        // can see through subtyping.
+        let ty_var_root = self.root_var(self_ty);
+        debug!("obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
+               self_ty, ty_var_root,
+               self.fulfillment_cx.borrow().pending_obligations());
+
+        self.fulfillment_cx
+            .borrow()
+            .pending_obligations()
+            .into_iter()
+            .filter_map(move |obligation| match obligation.predicate {
+                ty::Predicate::Projection(ref data) =>
+                    Some((data.to_poly_trait_ref(self.tcx), obligation)),
+                ty::Predicate::Trait(ref data) =>
+                    Some((data.to_poly_trait_ref(), obligation)),
+                ty::Predicate::Subtype(..) => None,
+                ty::Predicate::RegionOutlives(..) => None,
+                ty::Predicate::TypeOutlives(..) => None,
+                ty::Predicate::WellFormed(..) => None,
+                ty::Predicate::ObjectSafe(..) => None,
+                ty::Predicate::ConstEvaluatable(..) => None,
+                // N.B., this predicate is created by breaking down a
+                // `ClosureType: FnFoo()` predicate, where
+                // `ClosureType` represents some `Closure`. It can't
+                // possibly be referring to the current closure,
+                // because we haven't produced the `Closure` for
+                // this closure yet; this is exactly why the other
+                // code is looking for a self type of a unresolved
+                // inference variable.
+                ty::Predicate::ClosureKind(..) => None,
+            }).filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
+    }
+
+    fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
+        self.obligations_for_self_ty(self_ty).any(|(tr, _)| {
+            Some(tr.def_id()) == self.tcx.lang_items().sized_trait()
+        })
+    }
+
+    /// Generic function that factors out common logic from function calls,
+    /// method calls and overloaded operators.
+    fn check_argument_types(
+        &self,
+        sp: Span,
+        expr: &'tcx hir::Expr,
+        fn_inputs: &[Ty<'tcx>],
+        expected_arg_tys: &[Ty<'tcx>],
+        args: &'tcx [hir::Expr],
+        c_variadic: bool,
+        tuple_arguments: TupleArgumentsFlag,
+        def_span: Option<Span>,
+    ) {
+        let tcx = self.tcx;
+        // Grab the argument types, supplying fresh type variables
+        // if the wrong number of arguments were supplied
+        let supplied_arg_count = if tuple_arguments == DontTupleArguments {
+            args.len()
         } else {
-            // Note: at this point, we cannot say what the best lifetime
-            // is to use for resulting pointer.  We want to use the
-            // shortest lifetime possible so as to avoid spurious borrowck
-            // errors.  Moreover, the longest lifetime will depend on the
-            // precise details of the value whose address is being taken
-            // (and how long it is valid), which we don't know yet until type
-            // inference is complete.
-            //
-            // Therefore, here we simply generate a region variable.  The
-            // region inferencer will then select the ultimate value.
-            // Finally, borrowck is charged with guaranteeing that the
-            // value whose address was taken can actually be made to live
-            // as long as it needs to live.
-            match oprnd.node {
-                // String literals are already, implicitly converted to slices.
-                //ast::ExprLit(lit) if ast_util::lit_is_str(lit) => fcx.expr_ty(oprnd),
-                // Empty slices live in static memory.
-                ast::ExprVec(ref elements) if elements.len() == 0 => {
-                    // Note: we do not assign a lifetime of
-                    // static. This is because the resulting type
-                    // `&'static [T]` would require that T outlives
-                    // `'static`!
-                    let region = fcx.infcx().next_region_var(
-                        infer::AddrOfSlice(expr.span));
-                    ty::mk_rptr(tcx, tcx.mk_region(region), tm)
-                }
-                _ => {
-                    let region = fcx.infcx().next_region_var(infer::AddrOfRegion(expr.span));
-                    ty::mk_rptr(tcx, tcx.mk_region(region), tm)
-                }
+            1
+        };
+
+        // All the input types from the fn signature must outlive the call
+        // so as to validate implied bounds.
+        for (fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
+            self.register_wf_obligation(fn_input_ty, arg_expr.span, traits::MiscObligation);
+        }
+
+        let expected_arg_count = fn_inputs.len();
+
+        let param_count_error = |expected_count: usize,
+                                 arg_count: usize,
+                                 error_code: &str,
+                                 c_variadic: bool,
+                                 sugg_unit: bool| {
+            let mut err = tcx.sess.struct_span_err_with_code(sp,
+                &format!("this function takes {}{} but {} {} supplied",
+                    if c_variadic { "at least " } else { "" },
+                    potentially_plural_count(expected_count, "parameter"),
+                    potentially_plural_count(arg_count, "parameter"),
+                    if arg_count == 1 {"was"} else {"were"}),
+                DiagnosticId::Error(error_code.to_owned()));
+
+            if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().def_span(sp)) {
+                err.span_label(def_s, "defined here");
+            }
+            if sugg_unit {
+                let sugg_span = tcx.sess.source_map().end_point(expr.span);
+                // remove closing `)` from the span
+                let sugg_span = sugg_span.shrink_to_lo();
+                err.span_suggestion(
+                    sugg_span,
+                    "expected the unit value `()`; create it with empty parentheses",
+                    String::from("()"),
+                    Applicability::MachineApplicable);
+            } else {
+                err.span_label(sp, format!("expected {}{}",
+                                           if c_variadic { "at least " } else { "" },
+                                           potentially_plural_count(expected_count, "parameter")));
             }
+            err.emit();
         };
-        fcx.write_ty(id, oprnd_t);
-      }
-      ast::ExprPath(ref pth) => {
-          let defn = lookup_def(fcx, pth.span, id);
-          let pty = type_scheme_for_def(fcx, expr.span, defn);
-          instantiate_path(fcx, pth, pty, defn, expr.span, expr.id);
-
-          // We always require that the type provided as the value for
-          // a type parameter outlives the moment of instantiation.
-          constrain_path_type_parameters(fcx, expr);
-      }
-      ast::ExprInlineAsm(ref ia) => {
-          for &(_, ref input) in ia.inputs.iter() {
-              check_expr(fcx, &**input);
-          }
-          for &(_, ref out, _) in ia.outputs.iter() {
-              check_expr(fcx, &**out);
-          }
-          fcx.write_nil(id);
-      }
-      ast::ExprMac(_) => tcx.sess.bug("unexpanded macro"),
-      ast::ExprBreak(_) => { fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); }
-      ast::ExprAgain(_) => { fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); }
-      ast::ExprRet(ref expr_opt) => {
-        match fcx.ret_ty {
-            ty::FnConverging(result_type) => {
-                match *expr_opt {
-                    None =>
-                        if let Err(_) = fcx.mk_eqty(false, infer::Misc(expr.span),
-                                                    result_type, ty::mk_nil(fcx.tcx())) {
-                            span_err!(tcx.sess, expr.span, E0069,
-                                "`return;` in function returning non-nil");
+
+        let mut expected_arg_tys = expected_arg_tys.to_vec();
+
+        let formal_tys = if tuple_arguments == TupleArguments {
+            let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]);
+            match tuple_type.kind {
+                ty::Tuple(arg_types) if arg_types.len() != args.len() => {
+                    param_count_error(arg_types.len(), args.len(), "E0057", false, false);
+                    expected_arg_tys = vec![];
+                    self.err_args(args.len())
+                }
+                ty::Tuple(arg_types) => {
+                    expected_arg_tys = match expected_arg_tys.get(0) {
+                        Some(&ty) => match ty.kind {
+                            ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
+                            _ => vec![],
                         },
-                    Some(ref e) => {
-                        check_expr_coercable_to_type(fcx, &**e, result_type);
-                    }
+                        None => vec![],
+                    };
+                    arg_types.iter().map(|k| k.expect_ty()).collect()
                 }
-            }
-            ty::FnDiverging => {
-                if let Some(ref e) = *expr_opt {
-                    check_expr(fcx, &**e);
+                _ => {
+                    span_err!(tcx.sess, sp, E0059,
+                        "cannot use call notation; the first type parameter \
+                         for the function trait is neither a tuple nor unit");
+                    expected_arg_tys = vec![];
+                    self.err_args(args.len())
                 }
-                span_err!(tcx.sess, expr.span, E0166,
-                    "`return` in a function declared as diverging");
             }
-        }
-        fcx.write_ty(id, fcx.infcx().next_diverging_ty_var());
-      }
-      ast::ExprParen(ref a) => {
-        check_expr_with_expectation_and_lvalue_pref(fcx,
-                                                    &**a,
-                                                    expected,
-                                                    lvalue_pref);
-        fcx.write_ty(id, fcx.expr_ty(&**a));
-      }
-      ast::ExprAssign(ref lhs, ref rhs) => {
-        check_expr_with_lvalue_pref(fcx, &**lhs, PreferMutLvalue);
-
-        let tcx = fcx.tcx();
-        if !ty::expr_is_lval(tcx, &**lhs) {
-            span_err!(tcx.sess, expr.span, E0070,
-                "illegal left-hand side expression");
-        }
+        } else if expected_arg_count == supplied_arg_count {
+            fn_inputs.to_vec()
+        } else if c_variadic {
+            if supplied_arg_count >= expected_arg_count {
+                fn_inputs.to_vec()
+            } else {
+                param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
+                expected_arg_tys = vec![];
+                self.err_args(supplied_arg_count)
+            }
+        } else {
+            // is the missing argument of type `()`?
+            let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 {
+                self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit()
+            } else if fn_inputs.len() == 1 && supplied_arg_count == 0 {
+                self.resolve_vars_if_possible(&fn_inputs[0]).is_unit()
+            } else {
+                false
+            };
+            param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
 
-        let lhs_ty = fcx.expr_ty(&**lhs);
-        check_expr_coercable_to_type(fcx, &**rhs, lhs_ty);
-        let rhs_ty = fcx.expr_ty(&**rhs);
+            expected_arg_tys = vec![];
+            self.err_args(supplied_arg_count)
+        };
 
-        fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized);
+        debug!("check_argument_types: formal_tys={:?}",
+               formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>());
 
-        if ty::type_is_error(lhs_ty) || ty::type_is_error(rhs_ty) {
-            fcx.write_error(id);
+        // If there is no expectation, expect formal_tys.
+        let expected_arg_tys = if !expected_arg_tys.is_empty() {
+            expected_arg_tys
         } else {
-            fcx.write_nil(id);
-        }
-      }
-      ast::ExprIf(ref cond, ref then_blk, ref opt_else_expr) => {
-        check_then_else(fcx, &**cond, &**then_blk, opt_else_expr.as_ref().map(|e| &**e),
-                        id, expr.span, expected);
-      }
-      ast::ExprIfLet(..) => {
-        tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
-      }
-      ast::ExprWhile(ref cond, ref body, _) => {
-        check_expr_has_type(fcx, &**cond, tcx.types.bool);
-        check_block_no_value(fcx, &**body);
-        let cond_ty = fcx.expr_ty(&**cond);
-        let body_ty = fcx.node_ty(body.id);
-        if ty::type_is_error(cond_ty) || ty::type_is_error(body_ty) {
-            fcx.write_error(id);
-        }
-        else {
-            fcx.write_nil(id);
-        }
-      }
-      ast::ExprWhileLet(..) => {
-        tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
-      }
-      ast::ExprForLoop(ref pat, ref head, ref block, _) => {
-        check_expr(fcx, &**head);
-        let typ = lookup_method_for_for_loop(fcx, &**head, expr.id);
-        vtable::select_new_fcx_obligations(fcx);
-
-        debug!("ExprForLoop each item has type {}",
-               fcx.infcx().resolve_type_vars_if_possible(&typ).repr(fcx.tcx()));
-
-        let pcx = pat_ctxt {
-            fcx: fcx,
-            map: pat_id_map(&tcx.def_map, &**pat),
+            formal_tys.clone()
         };
-        _match::check_pat(&pcx, &**pat, typ);
-
-        check_block_no_value(fcx, &**block);
-        fcx.write_nil(id);
-      }
-      ast::ExprLoop(ref body, _) => {
-        check_block_no_value(fcx, &**body);
-        if !may_break(tcx, expr.id, &**body) {
-            fcx.write_ty(id, fcx.infcx().next_diverging_ty_var());
-        } else {
-            fcx.write_nil(id);
-        }
-      }
-      ast::ExprMatch(ref discrim, ref arms, match_src) => {
-        _match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src);
-      }
-      ast::ExprClosure(capture, opt_kind, ref decl, ref body) => {
-          closure::check_expr_closure(fcx, expr, capture, opt_kind, &**decl, &**body, expected);
-      }
-      ast::ExprBlock(ref b) => {
-        check_block_with_expected(fcx, &**b, expected);
-        fcx.write_ty(id, fcx.node_ty(b.id));
-      }
-      ast::ExprCall(ref callee, ref args) => {
-          callee::check_call(fcx, expr, &**callee, args.as_slice());
-      }
-      ast::ExprMethodCall(ident, ref tps, ref args) => {
-        check_method_call(fcx, expr, ident, args.as_slice(), tps.as_slice(), lvalue_pref);
-        let arg_tys = args.iter().map(|a| fcx.expr_ty(&**a));
-        let  args_err = arg_tys.fold(false,
-             |rest_err, a| {
-              rest_err || ty::type_is_error(a)});
-        if args_err {
-            fcx.write_error(id);
-        }
-      }
-      ast::ExprCast(ref e, ref t) => {
-        if let ast::TyFixedLengthVec(_, ref count_expr) = t.node {
-            check_expr_with_hint(fcx, &**count_expr, tcx.types.uint);
-        }
-        check_cast(fcx, expr, &**e, &**t);
-      }
-      ast::ExprVec(ref args) => {
-        let uty = expected.map_to_option(fcx, |uty| {
-            match uty.sty {
-                ty::ty_vec(ty, _) => Some(ty),
-                _ => None
-            }
-        });
 
-        let typ = match uty {
-            Some(uty) => {
-                for e in args.iter() {
-                    check_expr_coercable_to_type(fcx, &**e, uty);
-                }
-                uty
+        let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
+
+        // Check the arguments.
+        // We do this in a pretty awful way: first we type-check any arguments
+        // that are not closures, then we type-check the closures. This is so
+        // that we have more information about the types of arguments when we
+        // type-check the functions. This isn't really the right way to do this.
+        for &check_closures in &[false, true] {
+            debug!("check_closures={}", check_closures);
+
+            // More awful hacks: before we check argument types, try to do
+            // an "opportunistic" vtable resolution of any trait bounds on
+            // the call. This helps coercions.
+            if check_closures {
+                self.select_obligations_where_possible(false, |errors| {
+                    self.point_at_type_arg_instead_of_call_if_possible(errors, expr);
+                    self.point_at_arg_instead_of_call_if_possible(
+                        errors,
+                        &final_arg_types[..],
+                        sp,
+                        &args,
+                    );
+                })
             }
-            None => {
-                let t: Ty = fcx.infcx().next_ty_var();
-                for e in args.iter() {
-                    check_expr_has_type(fcx, &**e, t);
+
+            // For C-variadic functions, we don't have a declared type for all of
+            // the arguments hence we only do our usual type checking with
+            // the arguments who's types we do know.
+            let t = if c_variadic {
+                expected_arg_count
+            } else if tuple_arguments == TupleArguments {
+                args.len()
+            } else {
+                supplied_arg_count
+            };
+            for (i, arg) in args.iter().take(t).enumerate() {
+                // Warn only for the first loop (the "no closures" one).
+                // Closure arguments themselves can't be diverging, but
+                // a previous argument can, e.g., `foo(panic!(), || {})`.
+                if !check_closures {
+                    self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
                 }
-                t
-            }
-        };
-        let typ = ty::mk_vec(tcx, typ, Some(args.len()));
-        fcx.write_ty(id, typ);
-      }
-      ast::ExprRepeat(ref element, ref count_expr) => {
-        check_expr_has_type(fcx, &**count_expr, tcx.types.uint);
-        let count = ty::eval_repeat_count(fcx.tcx(), &**count_expr);
-
-        let uty = match expected {
-            ExpectHasType(uty) => {
-                match uty.sty {
-                    ty::ty_vec(ty, _) => Some(ty),
-                    _ => None
+
+                let is_closure = match arg.kind {
+                    ExprKind::Closure(..) => true,
+                    _ => false
+                };
+
+                if is_closure != check_closures {
+                    continue;
                 }
-            }
-            _ => None
-        };
 
-        let (element_ty, t) = match uty {
-            Some(uty) => {
-                check_expr_coercable_to_type(fcx, &**element, uty);
-                (uty, uty)
-            }
-            None => {
-                let t: Ty = fcx.infcx().next_ty_var();
-                check_expr_has_type(fcx, &**element, t);
-                (fcx.expr_ty(&**element), t)
+                debug!("checking the argument");
+                let formal_ty = formal_tys[i];
+
+                // The special-cased logic below has three functions:
+                // 1. Provide as good of an expected type as possible.
+                let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]);
+
+                let checked_ty = self.check_expr_with_expectation(&arg, expected);
+
+                // 2. Coerce to the most detailed type that could be coerced
+                //    to, which is `expected_ty` if `rvalue_hint` returns an
+                //    `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
+                let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty);
+                // We're processing function arguments so we definitely want to use
+                // two-phase borrows.
+                self.demand_coerce(&arg, checked_ty, coerce_ty, AllowTwoPhase::Yes);
+                final_arg_types.push((i, checked_ty, coerce_ty));
+
+                // 3. Relate the expected type and the formal one,
+                //    if the expected type was used for the coercion.
+                self.demand_suptype(arg.span, formal_ty, coerce_ty);
             }
-        };
-
-        if count > 1 {
-            // For [foo, ..n] where n > 1, `foo` must have
-            // Copy type:
-            fcx.require_type_meets(
-                t,
-                expr.span,
-                traits::RepeatVec,
-                ty::BoundCopy);
         }
 
-        if ty::type_is_error(element_ty) {
-            fcx.write_error(id);
-        } else {
-            let t = ty::mk_vec(tcx, t, Some(count));
-            fcx.write_ty(id, t);
-        }
-      }
-      ast::ExprTup(ref elts) => {
-        let expected = expected.only_has_type();
-        let flds = expected.map_to_option(fcx, |ty| {
-            match ty.sty {
-                ty::ty_tup(ref flds) => Some(&flds[]),
-                _ => None
+        // We also need to make sure we at least write the ty of the other
+        // arguments which we skipped above.
+        if c_variadic {
+            fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
+                use crate::structured_errors::{VariadicError, StructuredDiagnostic};
+                VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
             }
-        });
-        let mut err_field = false;
-
-        let elt_ts = elts.iter().enumerate().map(|(i, e)| {
-            let t = match flds {
-                Some(ref fs) if i < fs.len() => {
-                    let ety = fs[i];
-                    check_expr_coercable_to_type(fcx, &**e, ety);
-                    ety
-                }
-                _ => {
-                    check_expr_with_expectation(fcx, &**e, NoExpectation);
-                    fcx.expr_ty(&**e)
+
+            for arg in args.iter().skip(expected_arg_count) {
+                let arg_ty = self.check_expr(&arg);
+
+                // There are a few types which get autopromoted when passed via varargs
+                // in C but we just error out instead and require explicit casts.
+                let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
+                match arg_ty.kind {
+                    ty::Float(ast::FloatTy::F32) => {
+                        variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
+                    }
+                    ty::Int(ast::IntTy::I8) | ty::Int(ast::IntTy::I16) | ty::Bool => {
+                        variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
+                    }
+                    ty::Uint(ast::UintTy::U8) | ty::Uint(ast::UintTy::U16) => {
+                        variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
+                    }
+                    ty::FnDef(..) => {
+                        let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
+                        let ptr_ty = self.resolve_vars_if_possible(&ptr_ty);
+                        variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
+                    }
+                    _ => {}
                 }
-            };
-            err_field = err_field || ty::type_is_error(t);
-            t
-        }).collect();
-        if err_field {
-            fcx.write_error(id);
-        } else {
-            let typ = ty::mk_tup(tcx, elt_ts);
-            fcx.write_ty(id, typ);
-        }
-      }
-      ast::ExprStruct(ref path, ref fields, ref base_expr) => {
-        // Resolve the path.
-        let def = tcx.def_map.borrow().get(&id).map(|i| *i);
-        let struct_id = match def {
-            Some(def::DefVariant(enum_id, variant_id, true)) => {
-                check_struct_enum_variant(fcx, id, expr.span, enum_id,
-                                          variant_id, &fields[]);
-                enum_id
             }
-            Some(def::DefTrait(def_id)) => {
-                span_err!(tcx.sess, path.span, E0159,
-                    "use of trait `{}` as a struct constructor",
-                    pprust::path_to_string(path));
-                check_struct_fields_on_error(fcx,
-                                             id,
-                                             &fields[],
-                                             base_expr);
-                def_id
-            },
-            Some(def) => {
-                // Verify that this was actually a struct.
-                let typ = ty::lookup_item_type(fcx.ccx.tcx, def.def_id());
-                match typ.ty.sty {
-                    ty::ty_struct(struct_did, _) => {
-                        check_struct_constructor(fcx,
-                                                 id,
-                                                 expr.span,
-                                                 struct_did,
-                                                 &fields[],
-                                                 base_expr.as_ref().map(|e| &**e));
+        }
+    }
+
+    fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
+        vec![self.tcx.types.err; len]
+    }
+
+    /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
+    /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
+    /// reference a type argument. The reason to walk also the checked type is that the coerced type
+    /// can be not easily comparable with predicate type (because of coercion). If the types match
+    /// for either checked or coerced type, and there's only *one* argument that does, we point at
+    /// the corresponding argument's expression span instead of the `fn` call path span.
+    fn point_at_arg_instead_of_call_if_possible(
+        &self,
+        errors: &mut Vec<traits::FulfillmentError<'_>>,
+        final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
+        call_sp: Span,
+        args: &'tcx [hir::Expr],
+    ) {
+        if !call_sp.desugaring_kind().is_some() {
+            // We *do not* do this for desugared call spans to keep good diagnostics when involving
+            // the `?` operator.
+            for error in errors {
+                if let ty::Predicate::Trait(predicate) = error.obligation.predicate {
+                    // Collect the argument position for all arguments that could have caused this
+                    // `FulfillmentError`.
+                    let mut referenced_in = final_arg_types.iter()
+                        .map(|(i, checked_ty, _)| (i, checked_ty))
+                        .chain(final_arg_types.iter().map(|(i, _, coerced_ty)| (i, coerced_ty)))
+                        .flat_map(|(i, ty)| {
+                            let ty = self.resolve_vars_if_possible(ty);
+                            // We walk the argument type because the argument's type could have
+                            // been `Option<T>`, but the `FulfillmentError` references `T`.
+                            ty.walk()
+                                .filter(|&ty| ty == predicate.skip_binder().self_ty())
+                                .map(move |_| *i)
+                        })
+                        .collect::<Vec<_>>();
+
+                    // Both checked and coerced types could have matched, thus we need to remove
+                    // duplicates.
+                    referenced_in.dedup();
+
+                    if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) {
+                        // We make sure that only *one* argument matches the obligation failure
+                        // and we assign the obligation's span to its expression's.
+                        error.obligation.cause.span = args[ref_in].span;
+                        error.points_at_arg_span = true;
                     }
-                    _ => {
-                        span_err!(tcx.sess, path.span, E0071,
-                            "`{}` does not name a structure",
-                            pprust::path_to_string(path));
-                        check_struct_fields_on_error(fcx,
-                                                     id,
-                                                     &fields[],
-                                                     base_expr);
+                }
+            }
+        }
+    }
+
+    /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
+    /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
+    /// were caused by them. If they were, we point at the corresponding type argument's span
+    /// instead of the `fn` call path span.
+    fn point_at_type_arg_instead_of_call_if_possible(
+        &self,
+        errors: &mut Vec<traits::FulfillmentError<'_>>,
+        call_expr: &'tcx hir::Expr,
+    ) {
+        if let hir::ExprKind::Call(path, _) = &call_expr.kind {
+            if let hir::ExprKind::Path(qpath) = &path.kind {
+                if let hir::QPath::Resolved(_, path) = &qpath {
+                    for error in errors {
+                        if let ty::Predicate::Trait(predicate) = error.obligation.predicate {
+                            // If any of the type arguments in this path segment caused the
+                            // `FullfillmentError`, point at its span (#61860).
+                            for arg in path.segments.iter()
+                                .filter_map(|seg| seg.args.as_ref())
+                                .flat_map(|a| a.args.iter())
+                            {
+                                if let hir::GenericArg::Type(hir_ty) = &arg {
+                                    if let hir::TyKind::Path(
+                                        hir::QPath::TypeRelative(..),
+                                    ) = &hir_ty.kind {
+                                        // Avoid ICE with associated types. As this is best
+                                        // effort only, it's ok to ignore the case. It
+                                        // would trigger in `is_send::<T::AssocType>();`
+                                        // from `typeck-default-trait-impl-assoc-type.rs`.
+                                    } else {
+                                        let ty = AstConv::ast_ty_to_ty(self, hir_ty);
+                                        let ty = self.resolve_vars_if_possible(&ty);
+                                        if ty == predicate.skip_binder().self_ty() {
+                                            error.obligation.cause.span = hir_ty.span;
+                                        }
+                                    }
+                                }
+                            }
+                        }
                     }
                 }
+            }
+        }
+    }
+
+    // AST fragment checking
+    fn check_lit(&self,
+                 lit: &hir::Lit,
+                 expected: Expectation<'tcx>)
+                 -> Ty<'tcx>
+    {
+        let tcx = self.tcx;
 
-                def.def_id()
+        match lit.node {
+            ast::LitKind::Str(..) => tcx.mk_static_str(),
+            ast::LitKind::ByteStr(ref v) => {
+                tcx.mk_imm_ref(tcx.lifetimes.re_static,
+                               tcx.mk_array(tcx.types.u8, v.len() as u64))
+            }
+            ast::LitKind::Byte(_) => tcx.types.u8,
+            ast::LitKind::Char(_) => tcx.types.char,
+            ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t),
+            ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t),
+            ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
+                let opt_ty = expected.to_option(self).and_then(|ty| {
+                    match ty.kind {
+                        ty::Int(_) | ty::Uint(_) => Some(ty),
+                        ty::Char => Some(tcx.types.u8),
+                        ty::RawPtr(..) => Some(tcx.types.usize),
+                        ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize),
+                        _ => None
+                    }
+                });
+                opt_ty.unwrap_or_else(|| self.next_int_var())
             }
-            _ => {
-                tcx.sess.span_bug(path.span,
-                                  "structure constructor wasn't resolved")
+            ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t),
+            ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
+                let opt_ty = expected.to_option(self).and_then(|ty| {
+                    match ty.kind {
+                        ty::Float(_) => Some(ty),
+                        _ => None
+                    }
+                });
+                opt_ty.unwrap_or_else(|| self.next_float_var())
             }
+            ast::LitKind::Bool(_) => tcx.types.bool,
+            ast::LitKind::Err(_) => tcx.types.err,
+        }
+    }
+
+    // Determine the `Self` type, using fresh variables for all variables
+    // declared on the impl declaration e.g., `impl<A,B> for Vec<(A,B)>`
+    // would return `($0, $1)` where `$0` and `$1` are freshly instantiated type
+    // variables.
+    pub fn impl_self_ty(&self,
+                        span: Span, // (potential) receiver for this impl
+                        did: DefId)
+                        -> TypeAndSubsts<'tcx> {
+        let ity = self.tcx.type_of(did);
+        debug!("impl_self_ty: ity={:?}", ity);
+
+        let substs = self.fresh_substs_for_item(span, did);
+        let substd_ty = self.instantiate_type_scheme(span, &substs, &ity);
+
+        TypeAndSubsts { substs: substs, ty: substd_ty }
+    }
+
+    /// Unifies the output type with the expected type early, for more coercions
+    /// and forward type information on the input expressions.
+    fn expected_inputs_for_expected_output(&self,
+                                           call_span: Span,
+                                           expected_ret: Expectation<'tcx>,
+                                           formal_ret: Ty<'tcx>,
+                                           formal_args: &[Ty<'tcx>])
+                                           -> Vec<Ty<'tcx>> {
+        let formal_ret = self.resolve_vars_with_obligations(formal_ret);
+        let ret_ty = match expected_ret.only_has_type(self) {
+            Some(ret) => ret,
+            None => return Vec::new()
         };
-
-        // Turn the path into a type and verify that that type unifies with
-        // the resulting structure type. This is needed to handle type
-        // parameters correctly.
-        let actual_structure_type = fcx.expr_ty(&*expr);
-        if !ty::type_is_error(actual_structure_type) {
-            let type_and_substs = fcx.instantiate_struct_literal_ty(struct_id, path);
-            match fcx.mk_subty(false,
-                               infer::Misc(path.span),
-                               actual_structure_type,
-                               type_and_substs.ty) {
-                Ok(()) => {}
-                Err(type_error) => {
-                    let type_error_description =
-                        ty::type_err_to_str(tcx, &type_error);
-                    fcx.tcx()
-                       .sess
-                       .span_err(path.span,
-                                 &format!("structure constructor specifies a \
-                                         structure of type `{}`, but this \
-                                         structure has type `{}`: {}",
-                                         fcx.infcx()
-                                            .ty_to_string(type_and_substs.ty),
-                                         fcx.infcx()
-                                            .ty_to_string(
-                                                actual_structure_type),
-                                         type_error_description)[]);
-                    ty::note_and_explain_type_err(tcx, &type_error);
+        let expect_args = self.fudge_inference_if_ok(|| {
+            // Attempt to apply a subtyping relationship between the formal
+            // return type (likely containing type variables if the function
+            // is polymorphic) and the expected return type.
+            // No argument expectations are produced if unification fails.
+            let origin = self.misc(call_span);
+            let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret);
+
+            // FIXME(#27336) can't use ? here, Try::from_error doesn't default
+            // to identity so the resulting type is not constrained.
+            match ures {
+                Ok(ok) => {
+                    // Process any obligations locally as much as
+                    // we can.  We don't care if some things turn
+                    // out unconstrained or ambiguous, as we're
+                    // just trying to get hints here.
+                    self.save_and_restore_in_snapshot_flag(|_| {
+                        let mut fulfill = TraitEngine::new(self.tcx);
+                        for obligation in ok.obligations {
+                            fulfill.register_predicate_obligation(self, obligation);
+                        }
+                        fulfill.select_where_possible(self)
+                    }).map_err(|_| ())?;
                 }
+                Err(_) => return Err(()),
             }
-        }
 
-        fcx.require_expr_have_sized_type(expr, traits::StructInitializerSized);
-      }
-      ast::ExprField(ref base, ref field) => {
-        check_field(fcx, expr, lvalue_pref, &**base, field);
-      }
-      ast::ExprTupField(ref base, idx) => {
-        check_tup_field(fcx, expr, lvalue_pref, &**base, idx);
-      }
-      ast::ExprIndex(ref base, ref idx) => {
-          check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref);
-          let base_t = fcx.expr_ty(&**base);
-          if ty::type_is_error(base_t) {
-              fcx.write_ty(id, base_t);
-          } else {
-              check_expr(fcx, &**idx);
-              let idx_t = fcx.expr_ty(&**idx);
-              if ty::type_is_error(idx_t) {
-                  fcx.write_ty(id, idx_t);
-              } else {
-                  let base_t = structurally_resolved_type(fcx, expr.span, base_t);
-
-                  let result =
-                      autoderef_for_index(fcx, &**base, base_t, lvalue_pref, |adj_ty, adj| {
-                          try_index_step(fcx,
-                                         MethodCall::expr(expr.id),
-                                         expr,
-                                         &**base,
-                                         adj_ty,
-                                         adj,
-                                         lvalue_pref,
-                                         idx_t)
-                      });
-
-                  match result {
-                      Some((index_ty, element_ty)) => {
-                          // FIXME: we've already checked idx above, we should
-                          // probably just demand subtype or something here.
-                          check_expr_has_type(fcx, &**idx, index_ty);
-                          fcx.write_ty(id, element_ty);
-                      }
-                      _ => {
-                          check_expr_has_type(fcx, &**idx, fcx.tcx().types.err);
-                          fcx.type_error_message(
-                              expr.span,
-                              |actual| {
-                                  format!("cannot index a value of type `{}`",
-                                          actual)
-                              },
-                              base_t,
-                              None);
-                          fcx.write_ty(id, fcx.tcx().types.err);
-                      }
-                  }
-              }
-          }
-       }
-       ast::ExprRange(ref start, ref end) => {
-          let t_start = start.as_ref().map(|e| {
-            check_expr(fcx, &**e);
-            fcx.expr_ty(&**e)
-          });
-          let t_end = end.as_ref().map(|e| {
-            check_expr(fcx, &**e);
-            fcx.expr_ty(&**e)
-          });
-
-          let idx_type = match (t_start, t_end) {
-              (Some(ty), None) | (None, Some(ty)) => {
-                  Some(ty)
-              }
-              (Some(t_start), Some(t_end)) if (ty::type_is_error(t_start) ||
-                                               ty::type_is_error(t_end)) => {
-                  Some(fcx.tcx().types.err)
-              }
-              (Some(t_start), Some(t_end)) => {
-                  Some(infer::common_supertype(fcx.infcx(),
-                                               infer::RangeExpression(expr.span),
-                                               true,
-                                               t_start,
-                                               t_end))
-              }
-              _ => None
-          };
-
-          // Note that we don't check the type of start/end satisfy any
-          // bounds because right now the range structs do not have any. If we add
-          // some bounds, then we'll need to check `t_start` against them here.
-
-          let range_type = match idx_type {
-            Some(idx_type) if ty::type_is_error(idx_type) => {
-                fcx.tcx().types.err
+            // Record all the argument types, with the substitutions
+            // produced from the above subtyping unification.
+            Ok(formal_args.iter().map(|ty| {
+                self.resolve_vars_if_possible(ty)
+            }).collect())
+        }).unwrap_or_default();
+        debug!("expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
+               formal_args, formal_ret,
+               expect_args, expected_ret);
+        expect_args
+    }
+
+    pub fn check_struct_path(&self,
+                             qpath: &QPath,
+                             hir_id: hir::HirId)
+                             -> Option<(&'tcx ty::VariantDef,  Ty<'tcx>)> {
+        let path_span = match *qpath {
+            QPath::Resolved(_, ref path) => path.span,
+            QPath::TypeRelative(ref qself, _) => qself.span
+        };
+        let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
+        let variant = match def {
+            Res::Err => {
+                self.set_tainted_by_errors();
+                return None;
             }
-            Some(idx_type) => {
-                // Find the did from the appropriate lang item.
-                let did = match (start, end) {
-                    (&Some(_), &Some(_)) => tcx.lang_items.range_struct(),
-                    (&Some(_), &None) => tcx.lang_items.range_from_struct(),
-                    (&None, &Some(_)) => tcx.lang_items.range_to_struct(),
-                    (&None, &None) => {
-                        tcx.sess.span_bug(expr.span, "full range should be dealt with above")
+            Res::Def(DefKind::Variant, _) => {
+                match ty.kind {
+                    ty::Adt(adt, substs) => {
+                        Some((adt.variant_of_res(def), adt.did, substs))
                     }
-                };
-
-                if let Some(did) = did {
-                    let polytype = ty::lookup_item_type(tcx, did);
-                    let substs = Substs::new_type(vec![idx_type], vec![]);
-                    let bounds = fcx.instantiate_bounds(expr.span, &substs, &polytype.generics);
-                    fcx.add_obligations_for_parameters(
-                        traits::ObligationCause::new(expr.span,
-                                                     fcx.body_id,
-                                                     traits::ItemObligation(did)),
-                        &bounds);
-
-                    ty::mk_struct(tcx, did, tcx.mk_substs(substs))
-                } else {
-                    tcx.sess.span_err(expr.span, "No lang item for range syntax");
-                    fcx.tcx().types.err
+                    _ => bug!("unexpected type: {:?}", ty)
                 }
             }
-            None => {
-                // Neither start nor end => FullRange
-                if let Some(did) = tcx.lang_items.full_range_struct() {
-                    let substs = Substs::new_type(vec![], vec![]);
-                    ty::mk_struct(tcx, did, tcx.mk_substs(substs))
-                } else {
-                    tcx.sess.span_err(expr.span, "No lang item for range syntax");
-                    fcx.tcx().types.err
+            Res::Def(DefKind::Struct, _)
+            | Res::Def(DefKind::Union, _)
+            | Res::Def(DefKind::TyAlias, _)
+            | Res::Def(DefKind::AssocTy, _)
+            | Res::SelfTy(..) => {
+                match ty.kind {
+                    ty::Adt(adt, substs) if !adt.is_enum() => {
+                        Some((adt.non_enum_variant(), adt.did, substs))
+                    }
+                    _ => None,
                 }
             }
-          };
-
-          fcx.write_ty(id, range_type);
-       }
-
-    }
-
-    debug!("type of expr({}) {} is...", expr.id,
-           syntax::print::pprust::expr_to_string(expr));
-    debug!("... {}, expected is {}",
-           ppaux::ty_to_string(tcx, fcx.expr_ty(expr)),
-           expected.repr(tcx));
+            _ => bug!("unexpected definition: {:?}", def)
+        };
 
-    unifier();
-}
+        if let Some((variant, did, substs)) = variant {
+            debug!("check_struct_path: did={:?} substs={:?}", did, substs);
+            self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
 
-fn constrain_path_type_parameters(fcx: &FnCtxt,
-                                  expr: &ast::Expr)
-{
-    fcx.opt_node_ty_substs(expr.id, |item_substs| {
-        fcx.add_default_region_param_bounds(&item_substs.substs, expr);
-    });
-}
+            // Check bounds on type arguments used in the path.
+            let (bounds, _) = self.instantiate_bounds(path_span, did, substs);
+            let cause = traits::ObligationCause::new(
+                path_span,
+                self.body_id,
+                traits::ItemObligation(did),
+            );
+            self.add_obligations_for_parameters(cause, &bounds);
 
-impl<'tcx> Expectation<'tcx> {
-    /// Provide an expectation for an rvalue expression given an *optional*
-    /// hint, which is not required for type safety (the resulting type might
-    /// be checked higher up, as is the case with `&expr` and `box expr`), but
-    /// is useful in determining the concrete type.
-    ///
-    /// The primary use case is where the expected type is a fat pointer,
-    /// like `&[int]`. For example, consider the following statement:
-    ///
-    ///    let x: &[int] = &[1, 2, 3];
-    ///
-    /// In this case, the expected type for the `&[1, 2, 3]` expression is
-    /// `&[int]`. If however we were to say that `[1, 2, 3]` has the
-    /// expectation `ExpectHasType([int])`, that would be too strong --
-    /// `[1, 2, 3]` does not have the type `[int]` but rather `[int; 3]`.
-    /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
-    /// to the type `&[int]`. Therefore, we propagate this more limited hint,
-    /// which still is useful, because it informs integer literals and the like.
-    /// See the test case `test/run-pass/coerce-expect-unsized.rs` and #20169
-    /// for examples of where this comes up,.
-    fn rvalue_hint(ty: Ty<'tcx>) -> Expectation<'tcx> {
-        match ty.sty {
-            ty::ty_vec(_, None) | ty::ty_trait(..) => {
-                ExpectRvalueLikeUnsized(ty)
-            }
-            _ => ExpectHasType(ty)
+            Some((variant, ty))
+        } else {
+            struct_span_err!(self.tcx.sess, path_span, E0071,
+                             "expected struct, variant or union type, found {}",
+                             ty.sort_string(self.tcx))
+                .span_label(path_span, "not a struct")
+                .emit();
+            None
         }
     }
 
-    fn only_has_type(self) -> Expectation<'tcx> {
-        match self {
-            ExpectHasType(t) => ExpectHasType(t),
-            _ => NoExpectation
+    // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
+    // The newly resolved definition is written into `type_dependent_defs`.
+    fn finish_resolving_struct_path(&self,
+                                    qpath: &QPath,
+                                    path_span: Span,
+                                    hir_id: hir::HirId)
+                                    -> (Res, Ty<'tcx>)
+    {
+        match *qpath {
+            QPath::Resolved(ref maybe_qself, ref path) => {
+                let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
+                let ty = AstConv::res_to_ty(self, self_ty, path, true);
+                (path.res, ty)
+            }
+            QPath::TypeRelative(ref qself, ref segment) => {
+                let ty = self.to_ty(qself);
+
+                let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind {
+                    path.res
+                } else {
+                    Res::Err
+                };
+                let result = AstConv::associated_path_to_ty(
+                    self,
+                    hir_id,
+                    path_span,
+                    ty,
+                    res,
+                    segment,
+                    true,
+                );
+                let ty = result.map(|(ty, _, _)| ty).unwrap_or(self.tcx().types.err);
+                let result = result.map(|(_, kind, def_id)| (kind, def_id));
+
+                // Write back the new resolution.
+                self.write_resolution(hir_id, result);
+
+                (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty)
+            }
         }
     }
 
-    // Resolves `expected` by a single level if it is a variable. If
-    // there is no expected type or resolution is not possible (e.g.,
-    // no constraints yet present), just returns `None`.
-    fn resolve<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
-        match self {
-            NoExpectation => {
-                NoExpectation
-            }
-            ExpectCastableToType(t) => {
-                ExpectCastableToType(
-                    fcx.infcx().resolve_type_vars_if_possible(&t))
+    /// Resolves an associated value path into a base type and associated constant, or method
+    /// resolution. The newly resolved definition is written into `type_dependent_defs`.
+    pub fn resolve_ty_and_res_ufcs<'b>(&self,
+                                       qpath: &'b QPath,
+                                       hir_id: hir::HirId,
+                                       span: Span)
+                                       -> (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment])
+    {
+        debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
+        let (ty, qself, item_segment) = match *qpath {
+            QPath::Resolved(ref opt_qself, ref path) => {
+                return (path.res,
+                        opt_qself.as_ref().map(|qself| self.to_ty(qself)),
+                        &path.segments[..]);
             }
-            ExpectHasType(t) => {
-                ExpectHasType(
-                    fcx.infcx().resolve_type_vars_if_possible(&t))
+            QPath::TypeRelative(ref qself, ref segment) => {
+                (self.to_ty(qself), qself, segment)
             }
-            ExpectRvalueLikeUnsized(t) => {
-                ExpectRvalueLikeUnsized(
-                    fcx.infcx().resolve_type_vars_if_possible(&t))
+        };
+        if let Some(&cached_result) = self.tables.borrow().type_dependent_defs().get(hir_id) {
+            // Return directly on cache hit. This is useful to avoid doubly reporting
+            // errors with default match binding modes. See #44614.
+            let def = cached_result.map(|(kind, def_id)| Res::Def(kind, def_id))
+                .unwrap_or(Res::Err);
+            return (def, Some(ty), slice::from_ref(&**item_segment));
+        }
+        let item_name = item_segment.ident;
+        let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| {
+            let result = match error {
+                method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
+                _ => Err(ErrorReported),
+            };
+            if item_name.name != kw::Invalid {
+                self.report_method_error(
+                    span,
+                    ty,
+                    item_name,
+                    SelfSource::QPath(qself),
+                    error,
+                    None,
+                ).map(|mut e| e.emit());
             }
-        }
-    }
+            result
+        });
 
-    fn map<'a, F>(self, fcx: &FnCtxt<'a, 'tcx>, unpack: F) -> Expectation<'tcx> where
-        F: FnOnce(Ty<'tcx>) -> Expectation<'tcx>
-    {
-        match self.resolve(fcx) {
-            NoExpectation => NoExpectation,
-            ExpectCastableToType(ty) |
-            ExpectHasType(ty) |
-            ExpectRvalueLikeUnsized(ty) => unpack(ty),
+        // Write back the new resolution.
+        self.write_resolution(hir_id, result);
+        (
+            result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err),
+            Some(ty),
+            slice::from_ref(&**item_segment),
+        )
+    }
+
+    pub fn check_decl_initializer(
+        &self,
+        local: &'tcx hir::Local,
+        init: &'tcx hir::Expr,
+    ) -> Ty<'tcx> {
+        // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
+        // for #42640 (default match binding modes).
+        //
+        // See #44848.
+        let ref_bindings = local.pat.contains_explicit_ref_binding();
+
+        let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty;
+        if let Some(m) = ref_bindings {
+            // Somewhat subtle: if we have a `ref` binding in the pattern,
+            // we want to avoid introducing coercions for the RHS. This is
+            // both because it helps preserve sanity and, in the case of
+            // ref mut, for soundness (issue #23116). In particular, in
+            // the latter case, we need to be clear that the type of the
+            // referent for the reference that results is *equal to* the
+            // type of the place it is referencing, and not some
+            // supertype thereof.
+            let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
+            self.demand_eqtype(init.span, local_ty, init_ty);
+            init_ty
+        } else {
+            self.check_expr_coercable_to_type(init, local_ty)
         }
     }
 
-    fn map_to_option<'a, O, F>(self, fcx: &FnCtxt<'a, 'tcx>, unpack: F) -> Option<O> where
-        F: FnOnce(Ty<'tcx>) -> Option<O>,
-    {
-        match self.resolve(fcx) {
-            NoExpectation => None,
-            ExpectCastableToType(ty) |
-            ExpectHasType(ty) |
-            ExpectRvalueLikeUnsized(ty) => unpack(ty),
-        }
-    }
-}
+    pub fn check_decl_local(&self, local: &'tcx hir::Local) {
+        let t = self.local_ty(local.span, local.hir_id).decl_ty;
+        self.write_ty(local.hir_id, t);
 
-impl<'tcx> Repr<'tcx> for Expectation<'tcx> {
-    fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
-        match *self {
-            NoExpectation => format!("NoExpectation"),
-            ExpectHasType(t) => format!("ExpectHasType({})",
-                                        t.repr(tcx)),
-            ExpectCastableToType(t) => format!("ExpectCastableToType({})",
-                                               t.repr(tcx)),
-            ExpectRvalueLikeUnsized(t) => format!("ExpectRvalueLikeUnsized({})",
-                                                  t.repr(tcx)),
+        if let Some(ref init) = local.init {
+            let init_ty = self.check_decl_initializer(local, &init);
+            self.overwrite_local_ty_if_err(local, t, init_ty);
         }
-    }
-}
 
-pub fn check_decl_initializer(fcx: &FnCtxt,
-                              nid: ast::NodeId,
-                              init: &ast::Expr)
-{
-    let local_ty = fcx.local_ty(init.span, nid);
-    check_expr_coercable_to_type(fcx, init, local_ty)
-}
-
-pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local)  {
-    let tcx = fcx.ccx.tcx;
-
-    let t = fcx.local_ty(local.span, local.id);
-    fcx.write_ty(local.id, t);
+        self.check_pat_top(&local.pat, t, None);
+        let pat_ty = self.node_ty(local.pat.hir_id);
+        self.overwrite_local_ty_if_err(local, t, pat_ty);
+    }
 
-    if let Some(ref init) = local.init {
-        check_decl_initializer(fcx, local.id, &**init);
-        let init_ty = fcx.expr_ty(&**init);
-        if ty::type_is_error(init_ty) {
-            fcx.write_ty(local.id, init_ty);
+    fn overwrite_local_ty_if_err(&self, local: &'tcx hir::Local, decl_ty: Ty<'tcx>, ty: Ty<'tcx>) {
+        if ty.references_error() {
+            // Override the types everywhere with `types.err` to avoid knock down errors.
+            self.write_ty(local.hir_id, ty);
+            self.write_ty(local.pat.hir_id, ty);
+            let local_ty = LocalTy {
+                decl_ty,
+                revealed_ty: ty,
+            };
+            self.locals.borrow_mut().insert(local.hir_id, local_ty);
+            self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
         }
     }
 
-    let pcx = pat_ctxt {
-        fcx: fcx,
-        map: pat_id_map(&tcx.def_map, &*local.pat),
-    };
-    _match::check_pat(&pcx, &*local.pat, t);
-    let pat_ty = fcx.node_ty(local.pat.id);
-    if ty::type_is_error(pat_ty) {
-        fcx.write_ty(local.id, pat_ty);
+    fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) {
+        err.span_suggestion_short(
+            span.shrink_to_hi(),
+            "consider using a semicolon here",
+            ";".to_string(),
+            Applicability::MachineApplicable,
+        );
     }
-}
 
-pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt)  {
-    let node_id;
-    let mut saw_bot = false;
-    let mut saw_err = false;
-    match stmt.node {
-      ast::StmtDecl(ref decl, id) => {
-        node_id = id;
-        match decl.node {
-          ast::DeclLocal(ref l) => {
-              check_decl_local(fcx, &**l);
-              let l_t = fcx.node_ty(l.id);
-              saw_bot = saw_bot || fcx.infcx().type_var_diverges(l_t);
-              saw_err = saw_err || ty::type_is_error(l_t);
-          }
-          ast::DeclItem(_) => {/* ignore for now */ }
-        }
-      }
-      ast::StmtExpr(ref expr, id) => {
-        node_id = id;
-        // Check with expected type of ()
-        check_expr_has_type(fcx, &**expr, ty::mk_nil(fcx.tcx()));
-        let expr_ty = fcx.expr_ty(&**expr);
-        saw_bot = saw_bot || fcx.infcx().type_var_diverges(expr_ty);
-        saw_err = saw_err || ty::type_is_error(expr_ty);
-      }
-      ast::StmtSemi(ref expr, id) => {
-        node_id = id;
-        check_expr(fcx, &**expr);
-        let expr_ty = fcx.expr_ty(&**expr);
-        saw_bot |= fcx.infcx().type_var_diverges(expr_ty);
-        saw_err |= ty::type_is_error(expr_ty);
-      }
-      ast::StmtMac(..) => fcx.ccx.tcx.sess.bug("unexpanded macro")
-    }
-    if saw_bot {
-        fcx.write_ty(node_id, fcx.infcx().next_diverging_ty_var());
-    }
-    else if saw_err {
-        fcx.write_error(node_id);
-    }
-    else {
-        fcx.write_nil(node_id)
-    }
-}
+    pub fn check_stmt(&self, stmt: &'tcx hir::Stmt) {
+        // Don't do all the complex logic below for `DeclItem`.
+        match stmt.kind {
+            hir::StmtKind::Item(..) => return,
+            hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
+        }
 
-pub fn check_block_no_value(fcx: &FnCtxt, blk: &ast::Block)  {
-    check_block_with_expected(fcx, blk, ExpectHasType(ty::mk_nil(fcx.tcx())));
-    let blkty = fcx.node_ty(blk.id);
-    if ty::type_is_error(blkty) {
-        fcx.write_error(blk.id);
-    } else {
-        let nilty = ty::mk_nil(fcx.tcx());
-        demand::suptype(fcx, blk.span, nilty, blkty);
-    }
-}
+        self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
 
-fn check_block_with_expected<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                       blk: &ast::Block,
-                                       expected: Expectation<'tcx>) {
-    let prev = {
-        let mut fcx_ps = fcx.ps.borrow_mut();
-        let unsafety_state = fcx_ps.recurse(blk);
-        replace(&mut *fcx_ps, unsafety_state)
-    };
+        // Hide the outer diverging and `has_errors` flags.
+        let old_diverges = self.diverges.get();
+        let old_has_errors = self.has_errors.get();
+        self.diverges.set(Diverges::Maybe);
+        self.has_errors.set(false);
 
-    let mut warned = false;
-    let mut any_diverges = false;
-    let mut any_err = false;
-    for s in blk.stmts.iter() {
-        check_stmt(fcx, &**s);
-        let s_id = ast_util::stmt_id(&**s);
-        let s_ty = fcx.node_ty(s_id);
-        if any_diverges && !warned && match s.node {
-            ast::StmtDecl(ref decl, _) => {
-                match decl.node {
-                    ast::DeclLocal(_) => true,
-                    _ => false,
-                }
+        match stmt.kind {
+            hir::StmtKind::Local(ref l) => {
+                self.check_decl_local(&l);
             }
-            ast::StmtExpr(_, _) | ast::StmtSemi(_, _) => true,
-            _ => false
-        } {
-            fcx.ccx
-                .tcx
-                .sess
-                .add_lint(lint::builtin::UNREACHABLE_CODE,
-                          s_id,
-                          s.span,
-                          "unreachable statement".to_string());
-            warned = true;
-        }
-        any_diverges = any_diverges || fcx.infcx().type_var_diverges(s_ty);
-        any_err = any_err || ty::type_is_error(s_ty);
-    }
-    match blk.expr {
-        None => if any_err {
-            fcx.write_error(blk.id);
-        } else if any_diverges {
-            fcx.write_ty(blk.id, fcx.infcx().next_diverging_ty_var());
-        } else {
-            fcx.write_nil(blk.id);
-        },
-        Some(ref e) => {
-            if any_diverges && !warned {
-                fcx.ccx
-                    .tcx
-                    .sess
-                    .add_lint(lint::builtin::UNREACHABLE_CODE,
-                              e.id,
-                              e.span,
-                              "unreachable expression".to_string());
-            }
-            let ety = match expected {
-                ExpectHasType(ety) => {
-                    check_expr_coercable_to_type(fcx, &**e, ety);
-                    ety
-                }
-                _ => {
-                    check_expr_with_expectation(fcx, &**e, expected);
-                    fcx.expr_ty(&**e)
-                }
-            };
+            // Ignore for now.
+            hir::StmtKind::Item(_) => {}
+            hir::StmtKind::Expr(ref expr) => {
+                // Check with expected type of `()`.
 
-            if any_err {
-                fcx.write_error(blk.id);
-            } else if any_diverges {
-                fcx.write_ty(blk.id, fcx.infcx().next_diverging_ty_var());
-            } else {
-                fcx.write_ty(blk.id, ety);
+                self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
+                    self.suggest_semicolon_at_end(expr.span, err);
+                });
+            }
+            hir::StmtKind::Semi(ref expr) => {
+                self.check_expr(&expr);
             }
         }
-    };
-
-    *fcx.ps.borrow_mut() = prev;
-}
-
-/// Checks a constant appearing in a type. At the moment this is just the
-/// length expression in a fixed-length vector, but someday it might be
-/// extended to type-level numeric literals.
-fn check_const_in_type<'a,'tcx>(ccx: &'a CrateCtxt<'a,'tcx>,
-                                expr: &ast::Expr,
-                                expected_type: Ty<'tcx>) {
-    let inh = static_inherited_fields(ccx);
-    let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(expected_type), expr.id);
-    check_const_with_ty(&fcx, expr.span, expr, expected_type);
-}
 
-fn check_const(ccx: &CrateCtxt,
-               sp: Span,
-               e: &ast::Expr,
-               id: ast::NodeId) {
-    let inh = static_inherited_fields(ccx);
-    let rty = ty::node_id_to_type(ccx.tcx, id);
-    let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id);
-    let declty = (*fcx.ccx.tcx.tcache.borrow())[local_def(id)].ty;
-    check_const_with_ty(&fcx, sp, e, declty);
-}
-
-fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                 _: Span,
-                                 e: &ast::Expr,
-                                 declty: Ty<'tcx>) {
-    // Gather locals in statics (because of block expressions).
-    // This is technically unnecessary because locals in static items are forbidden,
-    // but prevents type checking from blowing up before const checking can properly
-    // emit a error.
-    GatherLocalsVisitor { fcx: fcx }.visit_expr(e);
-
-    check_expr_with_hint(fcx, e, declty);
-    demand::coerce(fcx, e.span, declty, e);
-    vtable::select_all_fcx_obligations_or_error(fcx);
-    regionck::regionck_expr(fcx, e);
-    writeback::resolve_type_vars_in_expr(fcx, e);
-}
-
-/// Checks whether a type can be represented in memory. In particular, it
-/// identifies types that contain themselves without indirection through a
-/// pointer, which would mean their size is unbounded. This is different from
-/// the question of whether a type can be instantiated. See the definition of
-/// `check_instantiable`.
-pub fn check_representable(tcx: &ty::ctxt,
-                           sp: Span,
-                           item_id: ast::NodeId,
-                           designation: &str) -> bool {
-    let rty = ty::node_id_to_type(tcx, item_id);
-
-    // Check that it is possible to represent this type. This call identifies
-    // (1) types that contain themselves and (2) types that contain a different
-    // recursive type. It is only necessary to throw an error on those that
-    // contain themselves. For case 2, there must be an inner type that will be
-    // caught by case 1.
-    match ty::is_type_representable(tcx, sp, rty) {
-      ty::SelfRecursive => {
-        span_err!(tcx.sess, sp, E0072,
-            "illegal recursive {} type; \
-             wrap the inner value in a box to make it representable",
-            designation);
-        return false
-      }
-      ty::Representable | ty::ContainsRecursive => (),
-    }
-    return true
-}
-
-/// Checks whether a type can be created without an instance of itself.
-/// This is similar but different from the question of whether a type
-/// can be represented.  For example, the following type:
-///
-///     enum foo { None, Some(foo) }
-///
-/// is instantiable but is not representable.  Similarly, the type
-///
-///     enum foo { Some(@foo) }
-///
-/// is representable, but not instantiable.
-pub fn check_instantiable(tcx: &ty::ctxt,
-                          sp: Span,
-                          item_id: ast::NodeId)
-                          -> bool {
-    let item_ty = ty::node_id_to_type(tcx, item_id);
-    if !ty::is_instantiable(tcx, item_ty) {
-        span_err!(tcx.sess, sp, E0073,
-            "this type cannot be instantiated without an \
-             instance of itself");
-        span_help!(tcx.sess, sp, "consider using `Option<{}>`",
-            ppaux::ty_to_string(tcx, item_ty));
-        false
-    } else {
-        true
+        // Combine the diverging and `has_error` flags.
+        self.diverges.set(self.diverges.get() | old_diverges);
+        self.has_errors.set(self.has_errors.get() | old_has_errors);
     }
-}
 
-pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) {
-    let t = ty::node_id_to_type(tcx, id);
-    if ty::type_needs_subst(t) {
-        span_err!(tcx.sess, sp, E0074, "SIMD vector cannot be generic");
-        return;
-    }
-    match t.sty {
-        ty::ty_struct(did, substs) => {
-            let fields = ty::lookup_struct_fields(tcx, did);
-            if fields.is_empty() {
-                span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty");
-                return;
-            }
-            let e = ty::lookup_field_type(tcx, did, fields[0].id, substs);
-            if !fields.iter().all(
-                         |f| ty::lookup_field_type(tcx, did, f.id, substs) == e) {
-                span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous");
-                return;
-            }
-            if !ty::type_is_machine(e) {
-                span_err!(tcx.sess, sp, E0077,
-                    "SIMD vector element type should be machine type");
-                return;
-            }
+    pub fn check_block_no_value(&self, blk: &'tcx hir::Block) {
+        let unit = self.tcx.mk_unit();
+        let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
+
+        // if the block produces a `!` value, that can always be
+        // (effectively) coerced to unit.
+        if !ty.is_never() {
+            self.demand_suptype(blk.span, unit, ty);
         }
-        _ => ()
     }
-}
 
-pub fn check_enum_variants(ccx: &CrateCtxt,
-                           sp: Span,
-                           vs: &[P<ast::Variant>],
-                           id: ast::NodeId) {
-
-    fn disr_in_range(ccx: &CrateCtxt,
-                     ty: attr::IntType,
-                     disr: ty::Disr) -> bool {
-        fn uint_in_range(ccx: &CrateCtxt, ty: ast::UintTy, disr: ty::Disr) -> bool {
-            match ty {
-                ast::TyU8 => disr as u8 as Disr == disr,
-                ast::TyU16 => disr as u16 as Disr == disr,
-                ast::TyU32 => disr as u32 as Disr == disr,
-                ast::TyU64 => disr as u64 as Disr == disr,
-                ast::TyUs(_) => uint_in_range(ccx, ccx.tcx.sess.target.uint_type, disr)
-            }
-        }
-        fn int_in_range(ccx: &CrateCtxt, ty: ast::IntTy, disr: ty::Disr) -> bool {
-            match ty {
-                ast::TyI8 => disr as i8 as Disr == disr,
-                ast::TyI16 => disr as i16 as Disr == disr,
-                ast::TyI32 => disr as i32 as Disr == disr,
-                ast::TyI64 => disr as i64 as Disr == disr,
-                ast::TyIs(_) => int_in_range(ccx, ccx.tcx.sess.target.int_type, disr)
+    /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
+    /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors
+    /// when given code like the following:
+    /// ```text
+    /// if false { return 0i32; } else { 1u32 }
+    /// //                               ^^^^ point at this instead of the whole `if` expression
+    /// ```
+    fn get_expr_coercion_span(&self, expr: &hir::Expr) -> syntax_pos::Span {
+        if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
+            let arm_spans: Vec<Span> = arms.iter().filter_map(|arm| {
+                self.in_progress_tables
+                    .and_then(|tables| tables.borrow().node_type_opt(arm.body.hir_id))
+                    .and_then(|arm_ty| {
+                        if arm_ty.is_never() {
+                            None
+                        } else {
+                            Some(match &arm.body.kind {
+                                // Point at the tail expression when possible.
+                                hir::ExprKind::Block(block, _) => block.expr
+                                    .as_ref()
+                                    .map(|e| e.span)
+                                    .unwrap_or(block.span),
+                                _ => arm.body.span,
+                            })
+                        }
+                    })
+            }).collect();
+            if arm_spans.len() == 1 {
+                return arm_spans[0];
             }
         }
-        match ty {
-            attr::UnsignedInt(ty) => uint_in_range(ccx, ty, disr),
-            attr::SignedInt(ty) => int_in_range(ccx, ty, disr)
-        }
+        expr.span
     }
 
-    fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                          vs: &[P<ast::Variant>],
-                          id: ast::NodeId,
-                          hint: attr::ReprAttr)
-                          -> Vec<Rc<ty::VariantInfo<'tcx>>> {
-
-        let rty = ty::node_id_to_type(ccx.tcx, id);
-        let mut variants: Vec<Rc<ty::VariantInfo>> = Vec::new();
-        let mut disr_vals: Vec<ty::Disr> = Vec::new();
-        let mut prev_disr_val: Option<ty::Disr> = None;
-
-        for v in vs.iter() {
+    fn check_block_with_expected(
+        &self,
+        blk: &'tcx hir::Block,
+        expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
+        let prev = {
+            let mut fcx_ps = self.ps.borrow_mut();
+            let unsafety_state = fcx_ps.recurse(blk);
+            replace(&mut *fcx_ps, unsafety_state)
+        };
 
-            // If the discriminant value is specified explicitly in the enum check whether the
-            // initialization expression is valid, otherwise use the last value plus one.
-            let mut current_disr_val = match prev_disr_val {
-                Some(prev_disr_val) => prev_disr_val + 1,
-                None => ty::INITIAL_DISCRIMINANT_VALUE
+        // In some cases, blocks have just one exit, but other blocks
+        // can be targeted by multiple breaks. This can happen both
+        // with labeled blocks as well as when we desugar
+        // a `try { ... }` expression.
+        //
+        // Example 1:
+        //
+        //    'a: { if true { break 'a Err(()); } Ok(()) }
+        //
+        // Here we would wind up with two coercions, one from
+        // `Err(())` and the other from the tail expression
+        // `Ok(())`. If the tail expression is omitted, that's a
+        // "forced unit" -- unless the block diverges, in which
+        // case we can ignore the tail expression (e.g., `'a: {
+        // break 'a 22; }` would not force the type of the block
+        // to be `()`).
+        let tail_expr = blk.expr.as_ref();
+        let coerce_to_ty = expected.coercion_target_type(self, blk.span);
+        let coerce = if blk.targeted_by_break {
+            CoerceMany::new(coerce_to_ty)
+        } else {
+            let tail_expr: &[P<hir::Expr>] = match tail_expr {
+                Some(e) => slice::from_ref(e),
+                None => &[],
             };
+            CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr)
+        };
 
-            match v.node.disr_expr {
-                Some(ref e) => {
-                    debug!("disr expr, checking {}", pprust::expr_to_string(&**e));
-
-                    let inh = static_inherited_fields(ccx);
-                    let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id);
-                    let declty = match hint {
-                        attr::ReprAny | attr::ReprPacked | attr::ReprExtern => fcx.tcx().types.int,
-                        attr::ReprInt(_, attr::SignedInt(ity)) => {
-                            ty::mk_mach_int(fcx.tcx(), ity)
-                        }
-                        attr::ReprInt(_, attr::UnsignedInt(ity)) => {
-                            ty::mk_mach_uint(fcx.tcx(), ity)
-                        },
-                    };
-                    check_const_with_ty(&fcx, e.span, &**e, declty);
-                    // check_expr (from check_const pass) doesn't guarantee
-                    // that the expression is in a form that eval_const_expr can
-                    // handle, so we may still get an internal compiler error
-
-                    match const_eval::eval_const_expr_partial(ccx.tcx, &**e) {
-                        Ok(const_eval::const_int(val)) => current_disr_val = val as Disr,
-                        Ok(const_eval::const_uint(val)) => current_disr_val = val as Disr,
-                        Ok(_) => {
-                            span_err!(ccx.tcx.sess, e.span, E0079,
-                                "expected signed integer constant");
-                        }
-                        Err(ref err) => {
-                            span_err!(ccx.tcx.sess, e.span, E0080,
-                                "expected constant: {}", *err);
-                        }
-                    }
-                },
-                None => ()
-            };
+        let prev_diverges = self.diverges.get();
+        let ctxt = BreakableCtxt {
+            coerce: Some(coerce),
+            may_break: false,
+        };
 
-            // Check for duplicate discriminant values
-            match disr_vals.iter().position(|&x| x == current_disr_val) {
-                Some(i) => {
-                    span_err!(ccx.tcx.sess, v.span, E0081,
-                        "discriminant value `{}` already exists", disr_vals[i]);
-                    span_note!(ccx.tcx.sess, ccx.tcx.map.span(variants[i].id.node),
-                        "conflicting discriminant here")
-                }
-                None => {}
+        let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
+            for s in &blk.stmts {
+                self.check_stmt(s);
             }
-            // Check for unrepresentable discriminant values
-            match hint {
-                attr::ReprAny | attr::ReprExtern => (),
-                attr::ReprInt(sp, ity) => {
-                    if !disr_in_range(ccx, ity, current_disr_val) {
-                        span_err!(ccx.tcx.sess, v.span, E0082,
-                            "discriminant value outside specified type");
-                        span_note!(ccx.tcx.sess, sp,
-                            "discriminant type specified here");
+
+            // check the tail expression **without** holding the
+            // `enclosing_breakables` lock below.
+            let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
+
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
+            let coerce = ctxt.coerce.as_mut().unwrap();
+            if let Some(tail_expr_ty) = tail_expr_ty {
+                let tail_expr = tail_expr.unwrap();
+                let span = self.get_expr_coercion_span(tail_expr);
+                let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
+                coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
+            } else {
+                // Subtle: if there is no explicit tail expression,
+                // that is typically equivalent to a tail expression
+                // of `()` -- except if the block diverges. In that
+                // case, there is no value supplied from the tail
+                // expression (assuming there are no other breaks,
+                // this implies that the type of the block will be
+                // `!`).
+                //
+                // #41425 -- label the implicit `()` as being the
+                // "found type" here, rather than the "expected type".
+                if !self.diverges.get().is_always() {
+                    // #50009 -- Do not point at the entire fn block span, point at the return type
+                    // span, as it is the cause of the requirement, and
+                    // `consider_hint_about_removing_semicolon` will point at the last expression
+                    // if it were a relevant part of the error. This improves usability in editors
+                    // that highlight errors inline.
+                    let mut sp = blk.span;
+                    let mut fn_span = None;
+                    if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
+                        let ret_sp = decl.output.span();
+                        if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
+                            // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
+                            // output would otherwise be incorrect and even misleading. Make sure
+                            // the span we're aiming at correspond to a `fn` body.
+                            if block_sp == blk.span {
+                                sp = ret_sp;
+                                fn_span = Some(ident.span);
+                            }
+                        }
                     }
-                }
-                attr::ReprPacked => {
-                    ccx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum");
+                    coerce.coerce_forced_unit(self, &self.misc(sp), &mut |err| {
+                        if let Some(expected_ty) = expected.only_has_type(self) {
+                            self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
+                        }
+                        if let Some(fn_span) = fn_span {
+                            err.span_label(
+                                fn_span,
+                                "implicitly returns `()` as its body has no tail or `return` \
+                                 expression",
+                            );
+                        }
+                    }, false);
                 }
             }
-            disr_vals.push(current_disr_val);
-
-            let variant_info = Rc::new(VariantInfo::from_ast_variant(ccx.tcx, &**v,
-                                                                     current_disr_val));
-            prev_disr_val = Some(current_disr_val);
+        });
 
-            variants.push(variant_info);
+        if ctxt.may_break {
+            // If we can break from the block, then the block's exit is always reachable
+            // (... as long as the entry is reachable) - regardless of the tail of the block.
+            self.diverges.set(prev_diverges);
         }
 
-        return variants;
-    }
-
-    let hint = *ty::lookup_repr_hints(ccx.tcx, ast::DefId { krate: ast::LOCAL_CRATE, node: id })
-        [].get(0).unwrap_or(&attr::ReprAny);
-
-    if hint != attr::ReprAny && vs.len() <= 1 {
-        if vs.len() == 1 {
-            span_err!(ccx.tcx.sess, sp, E0083,
-                "unsupported representation for univariant enum");
-        } else {
-            span_err!(ccx.tcx.sess, sp, E0084,
-                "unsupported representation for zero-variant enum");
-        };
-    }
-
-    let variants = do_check(ccx, vs, id, hint);
-
-    // cache so that ty::enum_variants won't repeat this work
-    ccx.tcx.enum_var_cache.borrow_mut().insert(local_def(id), Rc::new(variants));
-
-    check_representable(ccx.tcx, sp, id, "enum");
+        let mut ty = ctxt.coerce.unwrap().complete(self);
 
-    // Check that it is possible to instantiate this enum:
-    //
-    // This *sounds* like the same that as representable, but it's
-    // not.  See def'n of `check_instantiable()` for details.
-    check_instantiable(ccx.tcx, sp, id);
-}
+        if self.has_errors.get() || ty.references_error() {
+            ty = self.tcx.types.err
+        }
 
-pub fn lookup_def(fcx: &FnCtxt, sp: Span, id: ast::NodeId) -> def::Def {
-    lookup_def_ccx(fcx.ccx, sp, id)
-}
+        self.write_ty(blk.hir_id, ty);
 
-// Returns the type parameter count and the type for the given definition.
-pub fn type_scheme_for_def<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                     sp: Span,
-                                     defn: def::Def)
-                                     -> TypeScheme<'tcx> {
-    match defn {
-      def::DefLocal(nid) | def::DefUpvar(nid, _, _) => {
-          let typ = fcx.local_ty(sp, nid);
-          return no_params(typ);
-      }
-      def::DefFn(id, _) | def::DefStaticMethod(id, _) | def::DefMethod(id, _, _) |
-      def::DefStatic(id, _) | def::DefVariant(_, id, _) |
-      def::DefStruct(id) | def::DefConst(id) => {
-        return ty::lookup_item_type(fcx.ccx.tcx, id);
-      }
-      def::DefTrait(_) |
-      def::DefTy(..) |
-      def::DefAssociatedTy(..) |
-      def::DefAssociatedPath(..) |
-      def::DefPrimTy(_) |
-      def::DefTyParam(..) => {
-        fcx.ccx.tcx.sess.span_bug(sp, "expected value, found type");
-      }
-      def::DefMod(..) | def::DefForeignMod(..) => {
-        fcx.ccx.tcx.sess.span_bug(sp, "expected value, found module");
-      }
-      def::DefUse(..) => {
-        fcx.ccx.tcx.sess.span_bug(sp, "expected value, found use");
-      }
-      def::DefRegion(..) => {
-        fcx.ccx.tcx.sess.span_bug(sp, "expected value, found region");
-      }
-      def::DefTyParamBinder(..) => {
-        fcx.ccx.tcx.sess.span_bug(sp, "expected value, found type parameter");
-      }
-      def::DefLabel(..) => {
-        fcx.ccx.tcx.sess.span_bug(sp, "expected value, found label");
-      }
-      def::DefSelfTy(..) => {
-        fcx.ccx.tcx.sess.span_bug(sp, "expected value, found self ty");
-      }
+        *self.ps.borrow_mut() = prev;
+        ty
     }
-}
 
-// Instantiates the given path, which must refer to an item with the given
-// number of type parameters and type.
-pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                  path: &ast::Path,
-                                  type_scheme: TypeScheme<'tcx>,
-                                  def: def::Def,
-                                  span: Span,
-                                  node_id: ast::NodeId) {
-    debug!("instantiate_path(path={}, def={}, node_id={}, type_scheme={})",
-           path.repr(fcx.tcx()),
-           def.repr(fcx.tcx()),
-           node_id,
-           type_scheme.repr(fcx.tcx()));
-
-    // We need to extract the type parameters supplied by the user in
-    // the path `path`. Due to the current setup, this is a bit of a
-    // tricky-process; the problem is that resolve only tells us the
-    // end-point of the path resolution, and not the intermediate steps.
-    // Luckily, we can (at least for now) deduce the intermediate steps
-    // just from the end-point.
-    //
-    // There are basically three cases to consider:
-    //
-    // 1. Reference to a *type*, such as a struct or enum:
-    //
-    //        mod a { struct Foo<T> { ... } }
-    //
-    //    Because we don't allow types to be declared within one
-    //    another, a path that leads to a type will always look like
-    //    `a::b::Foo<T>` where `a` and `b` are modules. This implies
-    //    that only the final segment can have type parameters, and
-    //    they are located in the TypeSpace.
-    //
-    //    *Note:* Generally speaking, references to types don't
-    //    actually pass through this function, but rather the
-    //    `ast_ty_to_ty` function in `astconv`. However, in the case
-    //    of struct patterns (and maybe literals) we do invoke
-    //    `instantiate_path` to get the general type of an instance of
-    //    a struct. (In these cases, there are actually no type
-    //    parameters permitted at present, but perhaps we will allow
-    //    them in the future.)
-    //
-    // 1b. Reference to a enum variant or tuple-like struct:
-    //
-    //        struct foo<T>(...)
-    //        enum E<T> { foo(...) }
-    //
-    //    In these cases, the parameters are declared in the type
-    //    space.
-    //
-    // 2. Reference to a *fn item*:
-    //
-    //        fn foo<T>() { }
-    //
-    //    In this case, the path will again always have the form
-    //    `a::b::foo::<T>` where only the final segment should have
-    //    type parameters. However, in this case, those parameters are
-    //    declared on a value, and hence are in the `FnSpace`.
-    //
-    // 3. Reference to a *method*:
-    //
-    //        impl<A> SomeStruct<A> {
-    //            fn foo<B>(...)
-    //        }
-    //
-    //    Here we can have a path like
-    //    `a::b::SomeStruct::<A>::foo::<B>`, in which case parameters
-    //    may appear in two places. The penultimate segment,
-    //    `SomeStruct::<A>`, contains parameters in TypeSpace, and the
-    //    final segment, `foo::<B>` contains parameters in fn space.
-    //
-    // The first step then is to categorize the segments appropriately.
-
-    assert!(path.segments.len() >= 1);
-    let mut segment_spaces: Vec<_>;
-    match def {
-        // Case 1 and 1b. Reference to a *type* or *enum variant*.
-        def::DefSelfTy(..) |
-        def::DefStruct(..) |
-        def::DefVariant(..) |
-        def::DefTyParamBinder(..) |
-        def::DefTy(..) |
-        def::DefAssociatedTy(..) |
-        def::DefAssociatedPath(..) |
-        def::DefTrait(..) |
-        def::DefPrimTy(..) |
-        def::DefTyParam(..) => {
-            // Everything but the final segment should have no
-            // parameters at all.
-            segment_spaces = repeat(None).take(path.segments.len() - 1).collect();
-            segment_spaces.push(Some(subst::TypeSpace));
-        }
-
-        // Case 2. Reference to a top-level value.
-        def::DefFn(..) |
-        def::DefConst(..) |
-        def::DefStatic(..) => {
-            segment_spaces = repeat(None).take(path.segments.len() - 1).collect();
-            segment_spaces.push(Some(subst::FnSpace));
-        }
-
-        // Case 3. Reference to a method.
-        def::DefStaticMethod(_, providence) |
-        def::DefMethod(_, _, providence) => {
-            assert!(path.segments.len() >= 2);
-
-            match providence {
-                def::FromTrait(trait_did) => {
-                    callee::check_legal_trait_for_method_call(fcx.ccx, span, trait_did)
+    fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
+        let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id));
+        match node {
+            Node::Item(&hir::Item {
+                kind: hir::ItemKind::Fn(_, _, body_id), ..
+            }) |
+            Node::ImplItem(&hir::ImplItem {
+                kind: hir::ImplItemKind::Method(_, body_id), ..
+            }) => {
+                let body = self.tcx.hir().body(body_id);
+                if let ExprKind::Block(block, _) = &body.value.kind {
+                    return Some(block.span);
                 }
-                def::FromImpl(_) => {}
             }
-
-            segment_spaces = repeat(None).take(path.segments.len() - 2).collect();
-            segment_spaces.push(Some(subst::TypeSpace));
-            segment_spaces.push(Some(subst::FnSpace));
+            _ => {}
         }
+        None
+    }
 
-        // Other cases. Various nonsense that really shouldn't show up
-        // here. If they do, an error will have been reported
-        // elsewhere. (I hope)
-        def::DefMod(..) |
-        def::DefForeignMod(..) |
-        def::DefLocal(..) |
-        def::DefUse(..) |
-        def::DefRegion(..) |
-        def::DefLabel(..) |
-        def::DefUpvar(..) => {
-            segment_spaces = repeat(None).take(path.segments.len()).collect();
-        }
+    /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
+    fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl, ast::Ident)> {
+        let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id));
+        self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
     }
-    assert_eq!(segment_spaces.len(), path.segments.len());
 
-    debug!("segment_spaces={:?}", segment_spaces);
+    /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
+    fn get_node_fn_decl(&self, node: Node<'tcx>) -> Option<(&'tcx hir::FnDecl, ast::Ident, bool)> {
+        match node {
+            Node::Item(&hir::Item {
+                ident, kind: hir::ItemKind::Fn(ref sig, ..), ..
+            }) => {
+                // This is less than ideal, it will not suggest a return type span on any
+                // method called `main`, regardless of whether it is actually the entry point,
+                // but it will still present it as the reason for the expected type.
+                Some((&sig.decl, ident, ident.name != sym::main))
+            }
+            Node::TraitItem(&hir::TraitItem {
+                ident, kind: hir::TraitItemKind::Method(ref sig, ..), ..
+            }) => Some((&sig.decl, ident, true)),
+            Node::ImplItem(&hir::ImplItem {
+                ident, kind: hir::ImplItemKind::Method(ref sig, ..), ..
+            }) => Some((&sig.decl, ident, false)),
+            _ => None,
+        }
+    }
 
-    // Next, examine the definition, and determine how many type
-    // parameters we expect from each space.
-    let type_defs = &type_scheme.generics.types;
-    let region_defs = &type_scheme.generics.regions;
+    /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a
+    /// suggestion can be made, `None` otherwise.
+    pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl, bool)> {
+        // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
+        // `while` before reaching it, as block tail returns are not available in them.
+        self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| {
+            let parent = self.tcx.hir().get(blk_id);
+            self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
+        })
+    }
 
-    // Now that we have categorized what space the parameters for each
-    // segment belong to, let's sort out the parameters that the user
-    // provided (if any) into their appropriate spaces. We'll also report
-    // errors if type parameters are provided in an inappropriate place.
-    let mut substs = Substs::empty();
-    for (opt_space, segment) in segment_spaces.iter().zip(path.segments.iter()) {
-        match *opt_space {
-            None => {
-                report_error_if_segment_contains_type_parameters(fcx, segment);
+    /// On implicit return expressions with mismatched types, provides the following suggestions:
+    ///
+    /// - Points out the method's return type as the reason for the expected type.
+    /// - Possible missing semicolon.
+    /// - Possible missing return type if the return type is the default, and not `fn main()`.
+    pub fn suggest_mismatched_types_on_tail(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &'tcx hir::Expr,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+        cause_span: Span,
+        blk_id: hir::HirId,
+    ) -> bool {
+        let expr = expr.peel_drop_temps();
+        self.suggest_missing_semicolon(err, expr, expected, cause_span);
+        let mut pointing_at_return_type = false;
+        if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
+            pointing_at_return_type = self.suggest_missing_return_type(
+                err, &fn_decl, expected, found, can_suggest);
+        }
+        pointing_at_return_type
+    }
+
+    /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
+    /// the ctor would successfully solve the type mismatch and if so, suggest it:
+    /// ```
+    /// fn foo(x: usize) -> usize { x }
+    /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
+    /// ```
+    fn suggest_fn_call(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) -> bool {
+        let hir = self.tcx.hir();
+        let (def_id, sig) = match found.kind {
+            ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
+            ty::Closure(def_id, substs) => {
+                // We don't use `closure_sig` to account for malformed closures like
+                // `|_: [_; continue]| {}` and instead we don't suggest anything.
+                let closure_sig_ty = substs.as_closure().sig_ty(def_id, self.tcx);
+                (def_id, match closure_sig_ty.kind {
+                    ty::FnPtr(sig) => sig,
+                    _ => return false,
+                })
             }
+            _ => return false,
+        };
 
-            Some(space) => {
-                push_explicit_parameters_from_segment_to_substs(fcx,
-                                                                space,
-                                                                path.span,
-                                                                type_defs,
-                                                                region_defs,
-                                                                segment,
-                                                                &mut substs);
+        let sig = self
+            .replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig)
+            .0;
+        let sig = self.normalize_associated_types_in(expr.span, &sig);
+        if self.can_coerce(sig.output(), expected) {
+            let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
+                (String::new(), Applicability::MachineApplicable)
+            } else {
+                ("...".to_string(), Applicability::HasPlaceholders)
+            };
+            let mut msg = "call this function";
+            match hir.get_if_local(def_id) {
+                Some(Node::Item(hir::Item {
+                    kind: ItemKind::Fn(.., body_id),
+                    ..
+                })) |
+                Some(Node::ImplItem(hir::ImplItem {
+                    kind: hir::ImplItemKind::Method(_, body_id),
+                    ..
+                })) |
+                Some(Node::TraitItem(hir::TraitItem {
+                    kind: hir::TraitItemKind::Method(.., hir::TraitMethod::Provided(body_id)),
+                    ..
+                })) => {
+                    let body = hir.body(*body_id);
+                    sugg_call = body.params.iter()
+                        .map(|param| match &param.pat.kind {
+                            hir::PatKind::Binding(_, _, ident, None)
+                            if ident.name != kw::SelfLower => ident.to_string(),
+                            _ => "_".to_string(),
+                        }).collect::<Vec<_>>().join(", ");
+                }
+                Some(Node::Expr(hir::Expr {
+                    kind: ExprKind::Closure(_, _, body_id, closure_span, _),
+                    span: full_closure_span,
+                    ..
+                })) => {
+                    if *full_closure_span == expr.span {
+                        return false;
+                    }
+                    err.span_label(*closure_span, "closure defined here");
+                    msg = "call this closure";
+                    let body = hir.body(*body_id);
+                    sugg_call = body.params.iter()
+                        .map(|param| match &param.pat.kind {
+                            hir::PatKind::Binding(_, _, ident, None)
+                            if ident.name != kw::SelfLower => ident.to_string(),
+                            _ => "_".to_string(),
+                        }).collect::<Vec<_>>().join(", ");
+                }
+                Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
+                    sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
+                    match hir.as_local_hir_id(def_id).and_then(|hir_id| hir.def_kind(hir_id)) {
+                        Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
+                            msg = "instantiate this tuple variant";
+                        }
+                        Some(hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, _)) => {
+                            msg = "instantiate this tuple struct";
+                        }
+                        _ => {}
+                    }
+                }
+                Some(Node::ForeignItem(hir::ForeignItem {
+                    kind: hir::ForeignItemKind::Fn(_, idents, _),
+                    ..
+                })) |
+                Some(Node::TraitItem(hir::TraitItem {
+                    kind: hir::TraitItemKind::Method(.., hir::TraitMethod::Required(idents)),
+                    ..
+                })) => sugg_call = idents.iter()
+                        .map(|ident| if ident.name != kw::SelfLower {
+                            ident.to_string()
+                        } else {
+                            "_".to_string()
+                        }).collect::<Vec<_>>()
+                        .join(", "),
+                _ => {}
+            }
+            if let Ok(code) = self.sess().source_map().span_to_snippet(expr.span) {
+                err.span_suggestion(
+                    expr.span,
+                    &format!("use parentheses to {}", msg),
+                    format!("{}({})", code, sugg_call),
+                    applicability,
+                );
+                return true;
             }
         }
+        false
     }
 
-    // Now we have to compare the types that the user *actually*
-    // provided against the types that were *expected*. If the user
-    // did not provide any types, then we want to substitute inference
-    // variables. If the user provided some types, we may still need
-    // to add defaults. If the user provided *too many* types, that's
-    // a problem.
-    for &space in ParamSpace::all().iter() {
-        adjust_type_parameters(fcx, span, space, type_defs, &mut substs);
-        assert_eq!(substs.types.len(space), type_defs.len(space));
-
-        adjust_region_parameters(fcx, span, space, region_defs, &mut substs);
-        assert_eq!(substs.regions().len(space), region_defs.len(space));
+    pub fn suggest_ref_or_into(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) {
+        if let Some((sp, msg, suggestion)) = self.check_ref(expr, found, expected) {
+            err.span_suggestion(
+                sp,
+                msg,
+                suggestion,
+                Applicability::MachineApplicable,
+            );
+        } else if let (ty::FnDef(def_id, ..), true) = (
+            &found.kind,
+            self.suggest_fn_call(err, expr, expected, found),
+        ) {
+            if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
+                let sp = self.sess().source_map().def_span(sp);
+                err.span_label(sp, &format!("{} defined here", found));
+            }
+        } else if !self.check_for_cast(err, expr, found, expected) {
+            let is_struct_pat_shorthand_field = self.is_hir_id_from_struct_pattern_shorthand_field(
+                expr.hir_id,
+                expr.span,
+            );
+            let methods = self.get_conversion_methods(expr.span, expected, found);
+            if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
+                let mut suggestions = iter::repeat(&expr_text).zip(methods.iter())
+                    .filter_map(|(receiver, method)| {
+                        let method_call = format!(".{}()", method.ident);
+                        if receiver.ends_with(&method_call) {
+                            None  // do not suggest code that is already there (#53348)
+                        } else {
+                            let method_call_list = [".to_vec()", ".to_string()"];
+                            let sugg = if receiver.ends_with(".clone()")
+                                    && method_call_list.contains(&method_call.as_str()) {
+                                let max_len = receiver.rfind(".").unwrap();
+                                format!("{}{}", &receiver[..max_len], method_call)
+                            } else {
+                                if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
+                                    format!("({}){}", receiver, method_call)
+                                } else {
+                                    format!("{}{}", receiver, method_call)
+                                }
+                            };
+                            Some(if is_struct_pat_shorthand_field {
+                                format!("{}: {}", receiver, sugg)
+                            } else {
+                                sugg
+                            })
+                        }
+                    }).peekable();
+                if suggestions.peek().is_some() {
+                    err.span_suggestions(
+                        expr.span,
+                        "try using a conversion method",
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
     }
 
-    // The things we are substituting into the type should not contain
-    // escaping late-bound regions, and nor should the base type scheme.
-    assert!(!substs.has_regions_escaping_depth(0));
-    assert!(!type_scheme.has_escaping_regions());
-
-    // Add all the obligations that are required, substituting and
-    // normalized appropriately.
-    let bounds = fcx.instantiate_bounds(span, &substs, &type_scheme.generics);
-    fcx.add_obligations_for_parameters(
-        traits::ObligationCause::new(span, fcx.body_id, traits::ItemObligation(def.def_id())),
-        &bounds);
-
-    // Substitute the values for the type parameters into the type of
-    // the referenced item.
-    let ty_substituted = fcx.instantiate_type_scheme(span, &substs, &type_scheme.ty);
-
-    fcx.write_ty(node_id, ty_substituted);
-    fcx.write_substs(node_id, ty::ItemSubsts { substs: substs });
-    return;
-
-    fn report_error_if_segment_contains_type_parameters(
-        fcx: &FnCtxt,
-        segment: &ast::PathSegment)
-    {
-        for typ in segment.parameters.types().iter() {
-            span_err!(fcx.tcx().sess, typ.span, E0085,
-                "type parameters may not appear here");
-            break;
+    /// When encountering the expected boxed value allocated in the stack, suggest allocating it
+    /// in the heap by calling `Box::new()`.
+    fn suggest_boxing_when_appropriate(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) {
+        if self.tcx.hir().is_const_context(expr.hir_id) {
+            // Do not suggest `Box::new` in const context.
+            return;
         }
-
-        for lifetime in segment.parameters.lifetimes().iter() {
-            span_err!(fcx.tcx().sess, lifetime.span, E0086,
-                "lifetime parameters may not appear here");
-            break;
+        if !expected.is_box() || found.is_box() {
+            return;
+        }
+        let boxed_found = self.tcx.mk_box(found);
+        if let (true, Ok(snippet)) = (
+            self.can_coerce(boxed_found, expected),
+            self.sess().source_map().span_to_snippet(expr.span),
+        ) {
+            err.span_suggestion(
+                expr.span,
+                "store this in the heap by calling `Box::new`",
+                format!("Box::new({})", snippet),
+                Applicability::MachineApplicable,
+            );
+            err.note("for more on the distinction between the stack and the \
+                        heap, read https://doc.rust-lang.org/book/ch15-01-box.html, \
+                        https://doc.rust-lang.org/rust-by-example/std/box.html, and \
+                        https://doc.rust-lang.org/std/boxed/index.html");
         }
     }
 
-    /// Finds the parameters that the user provided and adds them to `substs`. If too many
-    /// parameters are provided, then reports an error and clears the output vector.
+
+    /// A common error is to forget to add a semicolon at the end of a block, e.g.,
     ///
-    /// We clear the output vector because that will cause the `adjust_XXX_parameters()` later to
-    /// use inference variables. This seems less likely to lead to derived errors.
+    /// ```
+    /// fn foo() {
+    ///     bar_that_returns_u32()
+    /// }
+    /// ```
     ///
-    /// Note that we *do not* check for *too few* parameters here. Due to the presence of defaults
-    /// etc that is more complicated. I wanted however to do the reporting of *too many* parameters
-    /// here because we can easily use the precise span of the N+1'th parameter.
-    fn push_explicit_parameters_from_segment_to_substs<'a, 'tcx>(
-        fcx: &FnCtxt<'a, 'tcx>,
-        space: subst::ParamSpace,
-        span: Span,
-        type_defs: &VecPerParamSpace<ty::TypeParameterDef<'tcx>>,
-        region_defs: &VecPerParamSpace<ty::RegionParameterDef>,
-        segment: &ast::PathSegment,
-        substs: &mut Substs<'tcx>)
-    {
-        match segment.parameters {
-            ast::AngleBracketedParameters(ref data) => {
-                push_explicit_angle_bracketed_parameters_from_segment_to_substs(
-                    fcx, space, type_defs, region_defs, data, substs);
+    /// This routine checks if the return expression in a block would make sense on its own as a
+    /// statement and the return type has been left as default or has been specified as `()`. If so,
+    /// it suggests adding a semicolon.
+    fn suggest_missing_semicolon(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expression: &'tcx hir::Expr,
+        expected: Ty<'tcx>,
+        cause_span: Span,
+    ) {
+        if expected.is_unit() {
+            // `BlockTailExpression` only relevant if the tail expr would be
+            // useful on its own.
+            match expression.kind {
+                ExprKind::Call(..) |
+                ExprKind::MethodCall(..) |
+                ExprKind::Loop(..) |
+                ExprKind::Match(..) |
+                ExprKind::Block(..) => {
+                    err.span_suggestion(
+                        cause_span.shrink_to_hi(),
+                        "try adding a semicolon",
+                        ";".to_string(),
+                        Applicability::MachineApplicable);
+                }
+                _ => (),
             }
+        }
+    }
 
-            ast::ParenthesizedParameters(ref data) => {
-                fcx.tcx().sess.span_err(
+    /// A possible error is to forget to add a return type that is needed:
+    ///
+    /// ```
+    /// fn foo() {
+    ///     bar_that_returns_u32()
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the return type is left as default, the method is not part of an
+    /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
+    /// type.
+    fn suggest_missing_return_type(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        fn_decl: &hir::FnDecl,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+        can_suggest: bool,
+    ) -> bool {
+        // Only suggest changing the return type for methods that
+        // haven't set a return type at all (and aren't `fn main()` or an impl).
+        match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
+            (&hir::FunctionRetTy::DefaultReturn(span), true, true, true) => {
+                err.span_suggestion(
                     span,
-                    "parenthesized parameters may only be used with a trait");
-                push_explicit_parenthesized_parameters_from_segment_to_substs(
-                    fcx, space, span, type_defs, data, substs);
+                    "try adding a return type",
+                    format!("-> {} ", self.resolve_vars_with_obligations(found)),
+                    Applicability::MachineApplicable);
+                true
+            }
+            (&hir::FunctionRetTy::DefaultReturn(span), false, true, true) => {
+                err.span_label(span, "possibly return type missing here?");
+                true
+            }
+            (&hir::FunctionRetTy::DefaultReturn(span), _, false, true) => {
+                // `fn main()` must return `()`, do not suggest changing return type
+                err.span_label(span, "expected `()` because of default return type");
+                true
+            }
+            // expectation was caused by something else, not the default return
+            (&hir::FunctionRetTy::DefaultReturn(_), _, _, false) => false,
+            (&hir::FunctionRetTy::Return(ref ty), _, _, _) => {
+                // Only point to return type if the expected type is the return type, as if they
+                // are not, the expectation must have been caused by something else.
+                debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
+                let sp = ty.span;
+                let ty = AstConv::ast_ty_to_ty(self, ty);
+                debug!("suggest_missing_return_type: return type {:?}", ty);
+                debug!("suggest_missing_return_type: expected type {:?}", ty);
+                if ty.kind == expected.kind {
+                    err.span_label(sp, format!("expected `{}` because of return type",
+                                               expected));
+                    return true;
+                }
+                false
             }
         }
     }
 
-    fn push_explicit_angle_bracketed_parameters_from_segment_to_substs<'a, 'tcx>(
-        fcx: &FnCtxt<'a, 'tcx>,
-        space: subst::ParamSpace,
-        type_defs: &VecPerParamSpace<ty::TypeParameterDef<'tcx>>,
-        region_defs: &VecPerParamSpace<ty::RegionParameterDef>,
-        data: &ast::AngleBracketedParameterData,
-        substs: &mut Substs<'tcx>)
-    {
-        {
-            let type_count = type_defs.len(space);
-            assert_eq!(substs.types.len(space), 0);
-            for (i, typ) in data.types.iter().enumerate() {
-                let t = fcx.to_ty(&**typ);
-                if i < type_count {
-                    substs.types.push(space, t);
-                } else if i == type_count {
-                    span_err!(fcx.tcx().sess, typ.span, E0087,
-                        "too many type parameters provided: \
-                         expected at most {} parameter(s), \
-                         found {} parameter(s)",
-                         type_count, data.types.len());
-                    substs.types.truncate(space, 0);
-                    break;
+    /// A possible error is to forget to add `.await` when using futures:
+    ///
+    /// ```
+    /// async fn make_u32() -> u32 {
+    ///     22
+    /// }
+    ///
+    /// fn take_u32(x: u32) {}
+    ///
+    /// async fn foo() {
+    ///     let x = make_u32();
+    ///     take_u32(x);
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+    /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+    /// `.await` to the tail of the expression.
+    fn suggest_missing_await(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        expr: &hir::Expr,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) {
+        // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
+        // body isn't `async`.
+        let item_id = self.tcx().hir().get_parent_node(self.body_id);
+        if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
+            let body = self.tcx().hir().body(body_id);
+            if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
+                let sp = expr.span;
+                // Check for `Future` implementations by constructing a predicate to
+                // prove: `<T as Future>::Output == U`
+                let future_trait = self.tcx.lang_items().future_trait().unwrap();
+                let item_def_id = self.tcx.associated_items(future_trait).next().unwrap().def_id;
+                let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
+                    // `<T as Future>::Output`
+                    projection_ty: ty::ProjectionTy {
+                        // `T`
+                        substs: self.tcx.mk_substs_trait(
+                            found,
+                            self.fresh_substs_for_item(sp, item_def_id)
+                        ),
+                        // `Future::Output`
+                        item_def_id,
+                    },
+                    ty: expected,
+                }));
+                let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
+                debug!("suggest_missing_await: trying obligation {:?}", obligation);
+                if self.infcx.predicate_may_hold(&obligation) {
+                    debug!("suggest_missing_await: obligation held: {:?}", obligation);
+                    if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
+                        err.span_suggestion(
+                            sp,
+                            "consider using `.await` here",
+                            format!("{}.await", code),
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        debug!("suggest_missing_await: no snippet for {:?}", sp);
+                    }
+                } else {
+                    debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
                 }
             }
         }
+    }
 
-        if data.bindings.len() > 0 {
-            span_err!(fcx.tcx().sess, data.bindings[0].span, E0182,
-                      "unexpected binding of associated item in expression path \
-                       (only allowed in type paths)");
+    /// A common error is to add an extra semicolon:
+    ///
+    /// ```
+    /// fn foo() -> usize {
+    ///     22;
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the final statement in a block is an
+    /// expression with an explicit semicolon whose type is compatible
+    /// with `expected_ty`. If so, it suggests removing the semicolon.
+    fn consider_hint_about_removing_semicolon(
+        &self,
+        blk: &'tcx hir::Block,
+        expected_ty: Ty<'tcx>,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
+        if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) {
+            err.span_suggestion(
+                span_semi,
+                "consider removing this semicolon",
+                String::new(),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+
+    fn could_remove_semicolon(&self, blk: &'tcx hir::Block, expected_ty: Ty<'tcx>) -> Option<Span> {
+        // Be helpful when the user wrote `{... expr;}` and
+        // taking the `;` off is enough to fix the error.
+        let last_stmt = blk.stmts.last()?;
+        let last_expr = match last_stmt.kind {
+            hir::StmtKind::Semi(ref e) => e,
+            _ => return None,
+        };
+        let last_expr_ty = self.node_ty(last_expr.hir_id);
+        if self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() {
+            return None;
         }
+        let original_span = original_sp(last_stmt.span, blk.span);
+        Some(original_span.with_lo(original_span.hi() - BytePos(1)))
+    }
 
-        {
-            let region_count = region_defs.len(space);
-            assert_eq!(substs.regions().len(space), 0);
-            for (i, lifetime) in data.lifetimes.iter().enumerate() {
-                let r = ast_region_to_region(fcx.tcx(), lifetime);
-                if i < region_count {
-                    substs.mut_regions().push(space, r);
-                } else if i == region_count {
-                    span_err!(fcx.tcx().sess, lifetime.span, E0088,
-                        "too many lifetime parameters provided: \
-                         expected {} parameter(s), found {} parameter(s)",
-                        region_count,
-                        data.lifetimes.len());
-                    substs.mut_regions().truncate(space, 0);
-                    break;
+    // Instantiates the given path, which must refer to an item with the given
+    // number of type parameters and type.
+    pub fn instantiate_value_path(&self,
+                                  segments: &[hir::PathSegment],
+                                  self_ty: Option<Ty<'tcx>>,
+                                  res: Res,
+                                  span: Span,
+                                  hir_id: hir::HirId)
+                                  -> (Ty<'tcx>, Res) {
+        debug!(
+            "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
+            segments,
+            self_ty,
+            res,
+            hir_id,
+        );
+
+        let tcx = self.tcx;
+
+        let path_segs = match res {
+            Res::Local(_) | Res::SelfCtor(_) => vec![],
+            Res::Def(kind, def_id) =>
+                AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id),
+            _ => bug!("instantiate_value_path on {:?}", res),
+        };
+
+        let mut user_self_ty = None;
+        let mut is_alias_variant_ctor = false;
+        match res {
+            Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => {
+                if let Some(self_ty) = self_ty {
+                    let adt_def = self_ty.ty_adt_def().unwrap();
+                    user_self_ty = Some(UserSelfTy {
+                        impl_def_id: adt_def.did,
+                        self_ty,
+                    });
+                    is_alias_variant_ctor = true;
                 }
             }
+            Res::Def(DefKind::Method, def_id)
+            | Res::Def(DefKind::AssocConst, def_id) => {
+                let container = tcx.associated_item(def_id).container;
+                debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
+                match container {
+                    ty::TraitContainer(trait_did) => {
+                        callee::check_legal_trait_for_method_call(tcx, span, trait_did)
+                    }
+                    ty::ImplContainer(impl_def_id) => {
+                        if segments.len() == 1 {
+                            // `<T>::assoc` will end up here, and so
+                            // can `T::assoc`. It this came from an
+                            // inherent impl, we need to record the
+                            // `T` for posterity (see `UserSelfTy` for
+                            // details).
+                            let self_ty = self_ty.expect("UFCS sugared assoc missing Self");
+                            user_self_ty = Some(UserSelfTy {
+                                impl_def_id,
+                                self_ty,
+                            });
+                        }
+                    }
+                }
+            }
+            _ => {}
         }
-    }
 
-    /// As with
-    /// `push_explicit_angle_bracketed_parameters_from_segment_to_substs`,
-    /// but intended for `Foo(A,B) -> C` form. This expands to
-    /// roughly the same thing as `Foo<(A,B),C>`. One important
-    /// difference has to do with the treatment of anonymous
-    /// regions, which are translated into bound regions (NYI).
-    fn push_explicit_parenthesized_parameters_from_segment_to_substs<'a, 'tcx>(
-        fcx: &FnCtxt<'a, 'tcx>,
-        space: subst::ParamSpace,
-        span: Span,
-        type_defs: &VecPerParamSpace<ty::TypeParameterDef<'tcx>>,
-        data: &ast::ParenthesizedParameterData,
-        substs: &mut Substs<'tcx>)
-    {
-        let type_count = type_defs.len(space);
-        if type_count < 2 {
-            span_err!(fcx.tcx().sess, span, E0167,
-                      "parenthesized form always supplies 2 type parameters, \
-                      but only {} parameter(s) were expected",
-                      type_count);
+        // Now that we have categorized what space the parameters for each
+        // segment belong to, let's sort out the parameters that the user
+        // provided (if any) into their appropriate spaces. We'll also report
+        // errors if type parameters are provided in an inappropriate place.
+
+        let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
+        let generics_has_err = AstConv::prohibit_generics(
+                self, segments.iter().enumerate().filter_map(|(index, seg)| {
+            if !generic_segs.contains(&index) || is_alias_variant_ctor {
+                Some(seg)
+            } else {
+                None
+            }
+        }));
+
+        if let Res::Local(hid) = res {
+            let ty = self.local_ty(span, hid).decl_ty;
+            let ty = self.normalize_associated_types_in(span, &ty);
+            self.write_ty(hir_id, ty);
+            return (ty, res);
+        }
+
+        if generics_has_err {
+            // Don't try to infer type parameters when prohibited generic arguments were given.
+            user_self_ty = None;
+        }
+
+        // Now we have to compare the types that the user *actually*
+        // provided against the types that were *expected*. If the user
+        // did not provide any types, then we want to substitute inference
+        // variables. If the user provided some types, we may still need
+        // to add defaults. If the user provided *too many* types, that's
+        // a problem.
+
+        let mut infer_args_for_err = FxHashSet::default();
+        for &PathSeg(def_id, index) in &path_segs {
+            let seg = &segments[index];
+            let generics = tcx.generics_of(def_id);
+            // Argument-position `impl Trait` is treated as a normal generic
+            // parameter internally, but we don't allow users to specify the
+            // parameter's value explicitly, so we have to do some error-
+            // checking here.
+            let suppress_errors = AstConv::check_generic_arg_count_for_call(
+                tcx,
+                span,
+                &generics,
+                &seg,
+                false, // `is_method_call`
+            );
+            if suppress_errors {
+                infer_args_for_err.insert(index);
+                self.set_tainted_by_errors(); // See issue #53251.
+            }
         }
 
-        let input_tys: Vec<Ty> =
-            data.inputs.iter().map(|ty| fcx.to_ty(&**ty)).collect();
+        let has_self = path_segs.last().map(|PathSeg(def_id, _)| {
+            tcx.generics_of(*def_id).has_self
+        }).unwrap_or(false);
+
+        let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res {
+            let ty = self.impl_self_ty(span, impl_def_id).ty;
+            let adt_def = ty.ty_adt_def();
+
+            match ty.kind {
+                ty::Adt(adt_def, substs) if adt_def.has_ctor() => {
+                    let variant = adt_def.non_enum_variant();
+                    let ctor_def_id = variant.ctor_def_id.unwrap();
+                    (
+                        Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id),
+                        Some(substs),
+                    )
+                }
+                _ => {
+                    let mut err = tcx.sess.struct_span_err(span,
+                        "the `Self` constructor can only be used with tuple or unit structs");
+                    if let Some(adt_def) = adt_def {
+                        match adt_def.adt_kind() {
+                            AdtKind::Enum => {
+                                err.help("did you mean to use one of the enum's variants?");
+                            },
+                            AdtKind::Struct |
+                            AdtKind::Union => {
+                                err.span_suggestion(
+                                    span,
+                                    "use curly brackets",
+                                    String::from("Self { /* fields */ }"),
+                                    Applicability::HasPlaceholders,
+                                );
+                            }
+                        }
+                    }
+                    err.emit();
 
-        let tuple_ty =
-            ty::mk_tup(fcx.tcx(), input_tys);
+                    return (tcx.types.err, res)
+                }
+            }
+        } else {
+            (res, None)
+        };
+        let def_id = res.def_id();
+
+        // The things we are substituting into the type should not contain
+        // escaping late-bound regions, and nor should the base type scheme.
+        let ty = tcx.type_of(def_id);
+
+        let substs = self_ctor_substs.unwrap_or_else(|| AstConv::create_substs_for_generic_args(
+            tcx,
+            def_id,
+            &[][..],
+            has_self,
+            self_ty,
+            // Provide the generic args, and whether types should be inferred.
+            |def_id| {
+                if let Some(&PathSeg(_, index)) = path_segs.iter().find(|&PathSeg(did, _)| {
+                    *did == def_id
+                }) {
+                    // If we've encountered an `impl Trait`-related error, we're just
+                    // going to infer the arguments for better error messages.
+                    if !infer_args_for_err.contains(&index) {
+                        // Check whether the user has provided generic arguments.
+                        if let Some(ref data) = segments[index].args {
+                            return (Some(data), segments[index].infer_args);
+                        }
+                    }
+                    return (None, segments[index].infer_args);
+                }
 
-        if type_count >= 1 {
-            substs.types.push(space, tuple_ty);
+                (None, true)
+            },
+            // Provide substitutions for parameters for which (valid) arguments have been provided.
+            |param, arg| {
+                match (&param.kind, arg) {
+                    (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
+                        AstConv::ast_region_to_region(self, lt, Some(param)).into()
+                    }
+                    (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
+                        self.to_ty(ty).into()
+                    }
+                    (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
+                        self.to_const(&ct.value, self.tcx.type_of(param.def_id)).into()
+                    }
+                    _ => unreachable!(),
+                }
+            },
+            // Provide substitutions for parameters for which arguments are inferred.
+            |substs, param, infer_args| {
+                match param.kind {
+                    GenericParamDefKind::Lifetime => {
+                        self.re_infer(Some(param), span).unwrap().into()
+                    }
+                    GenericParamDefKind::Type { has_default, .. } => {
+                        if !infer_args && has_default {
+                            // If we have a default, then we it doesn't matter that we're not
+                            // inferring the type arguments: we provide the default where any
+                            // is missing.
+                            let default = tcx.type_of(param.def_id);
+                            self.normalize_ty(
+                                span,
+                                default.subst_spanned(tcx, substs.unwrap(), Some(span))
+                            ).into()
+                        } else {
+                            // If no type arguments were provided, we have to infer them.
+                            // This case also occurs as a result of some malformed input, e.g.
+                            // a lifetime argument being given instead of a type parameter.
+                            // Using inference instead of `Error` gives better error messages.
+                            self.var_for_def(span, param)
+                        }
+                    }
+                    GenericParamDefKind::Const => {
+                        // FIXME(const_generics:defaults)
+                        // No const parameters were provided, we have to infer them.
+                        self.var_for_def(span, param)
+                    }
+                }
+            },
+        ));
+        assert!(!substs.has_escaping_bound_vars());
+        assert!(!ty.has_escaping_bound_vars());
+
+        // First, store the "user substs" for later.
+        self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
+
+        self.add_required_obligations(span, def_id, &substs);
+
+        // Substitute the values for the type parameters into the type of
+        // the referenced item.
+        let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty);
+
+        if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
+            // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
+            // is inherent, there is no `Self` parameter; instead, the impl needs
+            // type parameters, which we can infer by unifying the provided `Self`
+            // with the substituted impl type.
+            // This also occurs for an enum variant on a type alias.
+            let ty = tcx.type_of(impl_def_id);
+
+            let impl_ty = self.instantiate_type_scheme(span, &substs, &ty);
+            match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) {
+                Ok(ok) => self.register_infer_ok_obligations(ok),
+                Err(_) => {
+                    self.tcx.sess.delay_span_bug(span, &format!(
+                        "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
+                        self_ty,
+                        impl_ty,
+                    ));
+                }
+            }
         }
 
-        let output_ty: Option<Ty> =
-            data.output.as_ref().map(|ty| fcx.to_ty(&**ty));
+        self.check_rustc_args_require_const(def_id, hir_id, span);
+
+        debug!("instantiate_value_path: type of {:?} is {:?}",
+               hir_id,
+               ty_substituted);
+        self.write_substs(hir_id, substs);
 
-        let output_ty =
-            output_ty.unwrap_or(ty::mk_nil(fcx.tcx()));
+        (ty_substituted, res)
+    }
+
+    /// Add all the obligations that are required, substituting and normalized appropriately.
+    fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
+        let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs);
 
-        if type_count >= 2 {
-            substs.types.push(space, output_ty);
+        for (i, mut obligation) in traits::predicates_for_generics(
+            traits::ObligationCause::new(
+                span,
+                self.body_id,
+                traits::ItemObligation(def_id),
+            ),
+            self.param_env,
+            &bounds,
+        ).into_iter().enumerate() {
+            // This makes the error point at the bound, but we want to point at the argument
+            if let Some(span) = spans.get(i) {
+                obligation.cause.code = traits::BindingObligation(def_id, *span);
+            }
+            self.register_predicate(obligation);
         }
     }
 
-    fn adjust_type_parameters<'a, 'tcx>(
-        fcx: &FnCtxt<'a, 'tcx>,
-        span: Span,
-        space: ParamSpace,
-        defs: &VecPerParamSpace<ty::TypeParameterDef<'tcx>>,
-        substs: &mut Substs<'tcx>)
-    {
-        let provided_len = substs.types.len(space);
-        let desired = defs.get_slice(space);
-        let required_len = desired.iter()
-                              .take_while(|d| d.default.is_none())
-                              .count();
-
-        debug!("adjust_type_parameters(space={:?}, \
-               provided_len={}, \
-               desired_len={}, \
-               required_len={})",
-               space,
-               provided_len,
-               desired.len(),
-               required_len);
-
-        // Enforced by `push_explicit_parameters_from_segment_to_substs()`.
-        assert!(provided_len <= desired.len());
-
-        // Nothing specified at all: supply inference variables for
-        // everything.
-        if provided_len == 0 {
-            substs.types.replace(space,
-                                 fcx.infcx().next_ty_vars(desired.len()));
-            return;
+    fn check_rustc_args_require_const(&self,
+                                      def_id: DefId,
+                                      hir_id: hir::HirId,
+                                      span: Span) {
+        // We're only interested in functions tagged with
+        // #[rustc_args_required_const], so ignore anything that's not.
+        if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
+            return
         }
 
-        // Too few parameters specified: report an error and use Err
-        // for everything.
-        if provided_len < required_len {
-            let qualifier =
-                if desired.len() != required_len { "at least " } else { "" };
-            span_err!(fcx.tcx().sess, span, E0089,
-                "too few type parameters provided: expected {}{} parameter(s) \
-                , found {} parameter(s)",
-                qualifier, required_len, provided_len);
-            substs.types.replace(space, repeat(fcx.tcx().types.err).take(desired.len()).collect());
-            return;
+        // If our calling expression is indeed the function itself, we're good!
+        // If not, generate an error that this can only be called directly.
+        if let Node::Expr(expr) = self.tcx.hir().get(
+            self.tcx.hir().get_parent_node(hir_id))
+        {
+            if let ExprKind::Call(ref callee, ..) = expr.kind {
+                if callee.hir_id == hir_id {
+                    return
+                }
+            }
         }
 
-        // Otherwise, add in any optional parameters that the user
-        // omitted. The case of *too many* parameters is handled
-        // already by
-        // push_explicit_parameters_from_segment_to_substs(). Note
-        // that the *default* type are expressed in terms of all prior
-        // parameters, so we have to substitute as we go with the
-        // partial substitution that we have built up.
-        for i in range(provided_len, desired.len()) {
-            let default = desired[i].default.unwrap();
-            let default = default.subst_spanned(fcx.tcx(), substs, Some(span));
-            substs.types.push(space, default);
+        self.tcx.sess.span_err(span, "this function can only be invoked \
+                                      directly, not through a function pointer");
+    }
+
+    /// Resolves `typ` by a single level if `typ` is a type variable.
+    /// If no resolution is possible, then an error is reported.
+    /// Numeric inference variables may be left unresolved.
+    pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty = self.resolve_vars_with_obligations(ty);
+        if !ty.is_ty_var() {
+            ty
+        } else {
+            if !self.is_tainted_by_errors() {
+                self.need_type_info_err((**self).body_id, sp, ty, E0282)
+                    .note("type must be known at this point")
+                    .emit();
+            }
+            self.demand_suptype(sp, self.tcx.types.err, ty);
+            self.tcx.types.err
         }
-        assert_eq!(substs.types.len(space), desired.len());
+    }
 
-        debug!("Final substs: {}", substs.repr(fcx.tcx()));
+    fn with_breakable_ctxt<F: FnOnce() -> R, R>(
+        &self,
+        id: hir::HirId,
+        ctxt: BreakableCtxt<'tcx>,
+        f: F,
+    ) -> (BreakableCtxt<'tcx>, R) {
+        let index;
+        {
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            index = enclosing_breakables.stack.len();
+            enclosing_breakables.by_id.insert(id, index);
+            enclosing_breakables.stack.push(ctxt);
+        }
+        let result = f();
+        let ctxt = {
+            let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
+            debug_assert!(enclosing_breakables.stack.len() == index + 1);
+            enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
+            enclosing_breakables.stack.pop().expect("missing breakable context")
+        };
+        (ctxt, result)
     }
 
-    fn adjust_region_parameters(
-        fcx: &FnCtxt,
+    /// Instantiate a QueryResponse in a probe context, without a
+    /// good ObligationCause.
+    fn probe_instantiate_query_response(
+        &self,
         span: Span,
-        space: ParamSpace,
-        defs: &VecPerParamSpace<ty::RegionParameterDef>,
-        substs: &mut Substs)
+        original_values: &OriginalQueryValues<'tcx>,
+        query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
+    ) -> InferResult<'tcx, Ty<'tcx>>
     {
-        let provided_len = substs.mut_regions().len(space);
-        let desired = defs.get_slice(space);
-
-        // Enforced by `push_explicit_parameters_from_segment_to_substs()`.
-        assert!(provided_len <= desired.len());
+        self.instantiate_query_response_and_region_obligations(
+            &traits::ObligationCause::misc(span, self.body_id),
+            self.param_env,
+            original_values,
+            query_result)
+    }
 
-        // If nothing was provided, just use inference variables.
-        if provided_len == 0 {
-            substs.mut_regions().replace(
-                space,
-                fcx.infcx().region_vars_for_defs(span, desired));
-            return;
-        }
+    /// Returns `true` if an expression is contained inside the LHS of an assignment expression.
+    fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool {
+        let mut contained_in_place = false;
 
-        // If just the right number were provided, everybody is happy.
-        if provided_len == desired.len() {
-            return;
+        while let hir::Node::Expr(parent_expr) =
+            self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
+        {
+            match &parent_expr.kind {
+                hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
+                    if lhs.hir_id == expr_id {
+                        contained_in_place = true;
+                        break;
+                    }
+                }
+                _ => (),
+            }
+            expr_id = parent_expr.hir_id;
         }
 
-        // Otherwise, too few were provided. Report an error and then
-        // use inference variables.
-        span_err!(fcx.tcx().sess, span, E0090,
-            "too few lifetime parameters provided: expected {} parameter(s), \
-             found {} parameter(s)",
-            desired.len(), provided_len);
-
-        substs.mut_regions().replace(
-            space,
-            fcx.infcx().region_vars_for_defs(span, desired));
+        contained_in_place
     }
 }
 
-// Resolves `typ` by a single level if `typ` is a type variable.  If no
-// resolution is possible, then an error is reported.
-pub fn structurally_resolved_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
-                                            mut ty: Ty<'tcx>) -> Ty<'tcx> {
-    // If `ty` is a type variable, see whether we already know what it is.
-    ty = fcx.infcx().shallow_resolve(ty);
+pub fn check_bounds_are_used<'tcx>(tcx: TyCtxt<'tcx>, generics: &ty::Generics, ty: Ty<'tcx>) {
+    let own_counts = generics.own_counts();
+    debug!(
+        "check_bounds_are_used(n_tys={}, n_cts={}, ty={:?})",
+        own_counts.types,
+        own_counts.consts,
+        ty
+    );
 
-    // If not, try resolve pending fcx obligations. Those can shed light.
-    //
-    // FIXME(#18391) -- This current strategy can lead to bad performance in
-    // extreme cases.  We probably ought to smarter in general about
-    // only resolving when we need help and only resolving obligations
-    // will actually help.
-    if ty::type_is_ty_var(ty) {
-        vtable::select_fcx_obligations_where_possible(fcx);
-        ty = fcx.infcx().shallow_resolve(ty);
-    }
-
-    // If not, error.
-    if ty::type_is_ty_var(ty) {
-        fcx.type_error_message(sp, |_actual| {
-            "the type of this value must be known in this \
-             context".to_string()
-        }, ty, None);
-        demand::suptype(fcx, sp, fcx.tcx().types.err, ty);
-        ty = fcx.tcx().types.err;
-    }
-
-    ty
-}
+    if own_counts.types == 0 {
+        return;
+    }
 
-// Returns true if b contains a break that can exit from b
-pub fn may_break(cx: &ty::ctxt, id: ast::NodeId, b: &ast::Block) -> bool {
-    // First: is there an unlabeled break immediately
-    // inside the loop?
-    (loop_query(&*b, |e| {
-        match *e {
-            ast::ExprBreak(_) => true,
-            _ => false
-        }
-    })) ||
-   // Second: is there a labeled break with label
-   // <id> nested anywhere inside the loop?
-    (block_query(b, |e| {
-        match e.node {
-            ast::ExprBreak(Some(_)) => {
-                match cx.def_map.borrow().get(&e.id) {
-                    Some(&def::DefLabel(loop_id)) if id == loop_id => true,
-                    _ => false,
-                }
-            }
-            _ => false
-        }}))
-}
+    // Make a vector of booleans initially `false`; set to `true` when used.
+    let mut types_used = vec![false; own_counts.types];
 
-pub fn check_bounds_are_used<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                                       span: Span,
-                                       tps: &OwnedSlice<ast::TyParam>,
-                                       ty: Ty<'tcx>) {
-    debug!("check_bounds_are_used(n_tps={}, ty={})",
-           tps.len(), ppaux::ty_to_string(ccx.tcx, ty));
-
-    // make a vector of booleans initially false, set to true when used
-    if tps.len() == 0u { return; }
-    let mut tps_used: Vec<_> = repeat(false).take(tps.len()).collect();
-
-    ty::walk_ty(ty, |t| {
-            match t.sty {
-                ty::ty_param(ParamTy {idx, ..}) => {
-                    debug!("Found use of ty param num {}", idx);
-                    tps_used[idx as uint] = true;
-                }
-                _ => ()
-            }
-        });
+    for leaf_ty in ty.walk() {
+        if let ty::Param(ty::ParamTy { index, .. }) = leaf_ty.kind {
+            debug!("found use of ty param num {}", index);
+            types_used[index as usize - own_counts.lifetimes] = true;
+        } else if let ty::Error = leaf_ty.kind {
+            // If there is already another error, do not emit
+            // an error for not using a type parameter.
+            assert!(tcx.sess.has_errors());
+            return;
+        }
+    }
 
-    for (i, b) in tps_used.iter().enumerate() {
-        if !*b {
-            span_err!(ccx.tcx.sess, span, E0091,
-                "type parameter `{}` is unused",
-                token::get_ident(tps[i].ident));
+    let types = generics.params.iter().filter(|param| match param.kind {
+        ty::GenericParamDefKind::Type { .. } => true,
+        _ => false,
+    });
+    for (&used, param) in types_used.iter().zip(types) {
+        if !used {
+            let id = tcx.hir().as_local_hir_id(param.def_id).unwrap();
+            let span = tcx.hir().span(id);
+            struct_span_err!(tcx.sess, span, E0091, "type parameter `{}` is unused", param.name)
+                .span_label(span, "unused type parameter")
+                .emit();
         }
     }
 }
 
-pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
-    fn param<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, n: u32) -> Ty<'tcx> {
-        let name = token::intern(format!("P{}", n).as_slice());
-        ty::mk_param(ccx.tcx, subst::FnSpace, n, name)
-    }
-
-    let tcx = ccx.tcx;
-    let name = token::get_ident(it.ident);
-    let (n_tps, inputs, output) = if name.get().starts_with("atomic_") {
-        let split : Vec<&str> = name.get().split('_').collect();
-        assert!(split.len() >= 2, "Atomic intrinsic not correct format");
-
-        //We only care about the operation here
-        let (n_tps, inputs, output) = match split[1] {
-            "cxchg" => (1, vec!(ty::mk_mut_ptr(tcx, param(ccx, 0)),
-                                param(ccx, 0),
-                                param(ccx, 0)),
-                        param(ccx, 0)),
-            "load" => (1, vec!(ty::mk_imm_ptr(tcx, param(ccx, 0))),
-                       param(ccx, 0)),
-            "store" => (1, vec!(ty::mk_mut_ptr(tcx, param(ccx, 0)), param(ccx, 0)),
-                        ty::mk_nil(tcx)),
-
-            "xchg" | "xadd" | "xsub" | "and"  | "nand" | "or" | "xor" | "max" |
-            "min"  | "umax" | "umin" => {
-                (1, vec!(ty::mk_mut_ptr(tcx, param(ccx, 0)), param(ccx, 0)),
-                 param(ccx, 0))
-            }
-            "fence" => {
-                (0, Vec::new(), ty::mk_nil(tcx))
-            }
-            op => {
-                span_err!(tcx.sess, it.span, E0092,
-                    "unrecognized atomic operation function: `{}`", op);
-                return;
-            }
-        };
-        (n_tps, inputs, ty::FnConverging(output))
-    } else if name.get() == "abort" || name.get() == "unreachable" {
-        (0, Vec::new(), ty::FnDiverging)
-    } else {
-        let (n_tps, inputs, output) = match name.get() {
-            "breakpoint" => (0, Vec::new(), ty::mk_nil(tcx)),
-            "size_of" |
-            "pref_align_of" | "min_align_of" => (1u, Vec::new(), ccx.tcx.types.uint),
-            "init" => (1u, Vec::new(), param(ccx, 0)),
-            "uninit" => (1u, Vec::new(), param(ccx, 0)),
-            "forget" => (1u, vec!( param(ccx, 0) ), ty::mk_nil(tcx)),
-            "transmute" => (2, vec!( param(ccx, 0) ), param(ccx, 1)),
-            "move_val_init" => {
-                (1u,
-                 vec!(
-                    ty::mk_mut_rptr(tcx,
-                                    tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
-                                                                  ty::BrAnon(0))),
-                                    param(ccx, 0)),
-                    param(ccx, 0)
-                  ),
-               ty::mk_nil(tcx))
-            }
-            "needs_drop" => (1u, Vec::new(), ccx.tcx.types.bool),
-            "owns_managed" => (1u, Vec::new(), ccx.tcx.types.bool),
-
-            "get_tydesc" => {
-              let tydesc_ty = match ty::get_tydesc_ty(ccx.tcx) {
-                  Ok(t) => t,
-                  Err(s) => { tcx.sess.span_fatal(it.span, &s[]); }
-              };
-              let td_ptr = ty::mk_ptr(ccx.tcx, ty::mt {
-                  ty: tydesc_ty,
-                  mutbl: ast::MutImmutable
-              });
-              (1u, Vec::new(), td_ptr)
-            }
-            "type_id" => {
-                let langid = ccx.tcx.lang_items.require(TypeIdLangItem);
-                match langid {
-                    Ok(did) => (1u,
-                                Vec::new(),
-                                ty::mk_struct(ccx.tcx, did,
-                                              ccx.tcx.mk_substs(subst::Substs::empty()))),
-                    Err(msg) => {
-                        tcx.sess.span_fatal(it.span, &msg[]);
-                    }
-                }
-            },
-            "offset" => {
-              (1,
-               vec!(
-                  ty::mk_ptr(tcx, ty::mt {
-                      ty: param(ccx, 0),
-                      mutbl: ast::MutImmutable
-                  }),
-                  ccx.tcx.types.int
-               ),
-               ty::mk_ptr(tcx, ty::mt {
-                   ty: param(ccx, 0),
-                   mutbl: ast::MutImmutable
-               }))
-            }
-            "copy_memory" | "copy_nonoverlapping_memory" |
-            "volatile_copy_memory" | "volatile_copy_nonoverlapping_memory" => {
-              (1,
-               vec!(
-                  ty::mk_ptr(tcx, ty::mt {
-                      ty: param(ccx, 0),
-                      mutbl: ast::MutMutable
-                  }),
-                  ty::mk_ptr(tcx, ty::mt {
-                      ty: param(ccx, 0),
-                      mutbl: ast::MutImmutable
-                  }),
-                  tcx.types.uint,
-               ),
-               ty::mk_nil(tcx))
-            }
-            "set_memory" | "volatile_set_memory" => {
-              (1,
-               vec!(
-                  ty::mk_ptr(tcx, ty::mt {
-                      ty: param(ccx, 0),
-                      mutbl: ast::MutMutable
-                  }),
-                  tcx.types.u8,
-                  tcx.types.uint,
-               ),
-               ty::mk_nil(tcx))
-            }
-            "sqrtf32" => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "sqrtf64" => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "powif32" => {
-               (0,
-                vec!( tcx.types.f32, tcx.types.i32 ),
-                tcx.types.f32)
-            }
-            "powif64" => {
-               (0,
-                vec!( tcx.types.f64, tcx.types.i32 ),
-                tcx.types.f64)
-            }
-            "sinf32" => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "sinf64" => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "cosf32" => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "cosf64" => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "powf32" => {
-               (0,
-                vec!( tcx.types.f32, tcx.types.f32 ),
-                tcx.types.f32)
-            }
-            "powf64" => {
-               (0,
-                vec!( tcx.types.f64, tcx.types.f64 ),
-                tcx.types.f64)
-            }
-            "expf32"   => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "expf64"   => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "exp2f32"  => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "exp2f64"  => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "logf32"   => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "logf64"   => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "log10f32" => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "log10f64" => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "log2f32"  => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "log2f64"  => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "fmaf32" => {
-                (0,
-                 vec!( tcx.types.f32, tcx.types.f32, tcx.types.f32 ),
-                 tcx.types.f32)
-            }
-            "fmaf64" => {
-                (0,
-                 vec!( tcx.types.f64, tcx.types.f64, tcx.types.f64 ),
-                 tcx.types.f64)
-            }
-            "fabsf32"      => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "fabsf64"      => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "copysignf32"  => (0, vec!( tcx.types.f32, tcx.types.f32 ), tcx.types.f32),
-            "copysignf64"  => (0, vec!( tcx.types.f64, tcx.types.f64 ), tcx.types.f64),
-            "floorf32"     => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "floorf64"     => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "ceilf32"      => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "ceilf64"      => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "truncf32"     => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "truncf64"     => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "rintf32"      => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "rintf64"      => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "nearbyintf32" => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "nearbyintf64" => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "roundf32"     => (0, vec!( tcx.types.f32 ), tcx.types.f32),
-            "roundf64"     => (0, vec!( tcx.types.f64 ), tcx.types.f64),
-            "ctpop8"       => (0, vec!( tcx.types.u8  ), tcx.types.u8),
-            "ctpop16"      => (0, vec!( tcx.types.u16 ), tcx.types.u16),
-            "ctpop32"      => (0, vec!( tcx.types.u32 ), tcx.types.u32),
-            "ctpop64"      => (0, vec!( tcx.types.u64 ), tcx.types.u64),
-            "ctlz8"        => (0, vec!( tcx.types.u8  ), tcx.types.u8),
-            "ctlz16"       => (0, vec!( tcx.types.u16 ), tcx.types.u16),
-            "ctlz32"       => (0, vec!( tcx.types.u32 ), tcx.types.u32),
-            "ctlz64"       => (0, vec!( tcx.types.u64 ), tcx.types.u64),
-            "cttz8"        => (0, vec!( tcx.types.u8  ), tcx.types.u8),
-            "cttz16"       => (0, vec!( tcx.types.u16 ), tcx.types.u16),
-            "cttz32"       => (0, vec!( tcx.types.u32 ), tcx.types.u32),
-            "cttz64"       => (0, vec!( tcx.types.u64 ), tcx.types.u64),
-            "bswap16"      => (0, vec!( tcx.types.u16 ), tcx.types.u16),
-            "bswap32"      => (0, vec!( tcx.types.u32 ), tcx.types.u32),
-            "bswap64"      => (0, vec!( tcx.types.u64 ), tcx.types.u64),
-
-            "volatile_load" =>
-                (1, vec!( ty::mk_imm_ptr(tcx, param(ccx, 0)) ), param(ccx, 0)),
-            "volatile_store" =>
-                (1, vec!( ty::mk_mut_ptr(tcx, param(ccx, 0)), param(ccx, 0) ), ty::mk_nil(tcx)),
-
-            "i8_add_with_overflow" | "i8_sub_with_overflow" | "i8_mul_with_overflow" =>
-                (0, vec!(tcx.types.i8, tcx.types.i8),
-                ty::mk_tup(tcx, vec!(tcx.types.i8, tcx.types.bool))),
-
-            "i16_add_with_overflow" | "i16_sub_with_overflow" | "i16_mul_with_overflow" =>
-                (0, vec!(tcx.types.i16, tcx.types.i16),
-                ty::mk_tup(tcx, vec!(tcx.types.i16, tcx.types.bool))),
-
-            "i32_add_with_overflow" | "i32_sub_with_overflow" | "i32_mul_with_overflow" =>
-                (0, vec!(tcx.types.i32, tcx.types.i32),
-                ty::mk_tup(tcx, vec!(tcx.types.i32, tcx.types.bool))),
-
-            "i64_add_with_overflow" | "i64_sub_with_overflow" | "i64_mul_with_overflow" =>
-                (0, vec!(tcx.types.i64, tcx.types.i64),
-                ty::mk_tup(tcx, vec!(tcx.types.i64, tcx.types.bool))),
-
-            "u8_add_with_overflow" | "u8_sub_with_overflow" | "u8_mul_with_overflow" =>
-                (0, vec!(tcx.types.u8, tcx.types.u8),
-                ty::mk_tup(tcx, vec!(tcx.types.u8, tcx.types.bool))),
-
-            "u16_add_with_overflow" | "u16_sub_with_overflow" | "u16_mul_with_overflow" =>
-                (0, vec!(tcx.types.u16, tcx.types.u16),
-                ty::mk_tup(tcx, vec!(tcx.types.u16, tcx.types.bool))),
-
-            "u32_add_with_overflow" | "u32_sub_with_overflow" | "u32_mul_with_overflow"=>
-                (0, vec!(tcx.types.u32, tcx.types.u32),
-                ty::mk_tup(tcx, vec!(tcx.types.u32, tcx.types.bool))),
-
-            "u64_add_with_overflow" | "u64_sub_with_overflow"  | "u64_mul_with_overflow" =>
-                (0, vec!(tcx.types.u64, tcx.types.u64),
-                ty::mk_tup(tcx, vec!(tcx.types.u64, tcx.types.bool))),
-
-            "return_address" => (0, vec![], ty::mk_imm_ptr(tcx, tcx.types.u8)),
-
-            "assume" => (0, vec![tcx.types.bool], ty::mk_nil(tcx)),
-
-            ref other => {
-                span_err!(tcx.sess, it.span, E0093,
-                    "unrecognized intrinsic function: `{}`", *other);
-                return;
-            }
-        };
-        (n_tps, inputs, ty::FnConverging(output))
-    };
-    let fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(ty::BareFnTy {
-        unsafety: ast::Unsafety::Unsafe,
-        abi: abi::RustIntrinsic,
-        sig: ty::Binder(FnSig {
-            inputs: inputs,
-            output: output,
-            variadic: false,
-        }),
-    }));
-    let i_ty = ty::lookup_item_type(ccx.tcx, local_def(it.id));
-    let i_n_tps = i_ty.generics.types.len(subst::FnSpace);
-    if i_n_tps != n_tps {
-        span_err!(tcx.sess, it.span, E0094,
-            "intrinsic has wrong number of type \
-             parameters: found {}, expected {}",
-             i_n_tps, n_tps);
-    } else {
-        require_same_types(tcx,
-                           None,
-                           false,
-                           it.span,
-                           i_ty.ty,
-                           fty,
-                           || {
-                format!("intrinsic has wrong type: expected `{}`",
-                        ppaux::ty_to_string(ccx.tcx, fty))
-            });
-    }
+fn fatally_break_rust(sess: &Session) {
+    let handler = sess.diagnostic();
+    handler.span_bug_no_panic(
+        MultiSpan::new(),
+        "It looks like you're trying to break rust; would you like some ICE?",
+    );
+    handler.note_without_error("the compiler expectedly panicked. this is a feature.");
+    handler.note_without_error(
+        "we would appreciate a joke overview: \
+        https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675"
+    );
+    handler.note_without_error(&format!("rustc {} running on {}",
+        option_env!("CFG_VERSION").unwrap_or("unknown_version"),
+        crate::session::config::host_triple(),
+    ));
+}
+
+fn potentially_plural_count(count: usize, word: &str) -> String {
+    format!("{} {}{}", count, word, pluralize!(count))
 }