]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_const_eval/eval.rs
Rollup merge of #41135 - japaric:unstable-docs, r=steveklabnik
[rust.git] / src / librustc_const_eval / eval.rs
index af4f63a05613eef94f20438b98343163cf7d0c35..54f5cff16ed6cb584b888d46983820938f5e9d1e 100644 (file)
@@ -11,7 +11,6 @@
 use rustc::middle::const_val::ConstVal::*;
 use rustc::middle::const_val::ConstVal;
 use self::ErrKind::*;
-use self::EvalHint::*;
 
 use rustc::hir::map as hir_map;
 use rustc::hir::map::blocks::FnLikeNode;
@@ -20,7 +19,7 @@
 use rustc::hir::def_id::DefId;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::util::IntTypeExt;
-use rustc::ty::subst::Substs;
+use rustc::ty::subst::{Substs, Subst};
 use rustc::traits::Reveal;
 use rustc::util::common::ErrorReported;
 use rustc::util::nodemap::DefIdMap;
@@ -28,8 +27,7 @@
 use graphviz::IntoCow;
 use syntax::ast;
 use rustc::hir::{self, Expr};
-use syntax::attr::IntType;
-use syntax_pos::Span;
+use syntax_pos::{Span, DUMMY_SP};
 
 use std::borrow::Cow;
 use std::cmp::Ordering;
 use rustc_const_math::*;
 use rustc_errors::DiagnosticBuilder;
 
+macro_rules! signal {
+    ($e:expr, $exn:expr) => {
+        return Err(ConstEvalErr { span: $e.span, kind: $exn })
+    }
+}
+
 macro_rules! math {
     ($e:expr, $op:expr) => {
         match $op {
             Ok(val) => val,
-            Err(e) => signal!($e, Math(e)),
+            Err(e) => signal!($e, ErrKind::from(e)),
         }
     }
 }
 
 fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                   variant_def: DefId)
-                                  -> Option<(&'tcx Expr, Option<&'a ty::TypeckTables<'tcx>>)> {
+                                  -> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)> {
     if let Some(variant_node_id) = tcx.hir.as_local_node_id(variant_def) {
         let enum_node_id = tcx.hir.get_parent(variant_node_id);
         if let Some(hir_map::NodeItem(it)) = tcx.hir.find(enum_node_id) {
@@ -58,7 +62,7 @@ fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         return variant.node.disr_expr.map(|e| {
                             let def_id = tcx.hir.body_owner_def_id(e);
                             (&tcx.hir.body(e).value,
-                             tcx.tables.borrow().get(&def_id).cloned())
+                             tcx.item_tables(def_id))
                         });
                     }
                 }
@@ -75,55 +79,41 @@ fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 /// This generally happens in late/trans const evaluation.
 pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                         def_id: DefId,
-                                        substs: Option<&'tcx Substs<'tcx>>)
+                                        substs: &'tcx Substs<'tcx>)
                                         -> Option<(&'tcx Expr,
-                                                   Option<&'a ty::TypeckTables<'tcx>>,
-                                                   Option<ty::Ty<'tcx>>)> {
+                                                   &'a ty::TypeckTables<'tcx>)> {
     if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
         match tcx.hir.find(node_id) {
             None => None,
             Some(hir_map::NodeItem(&hir::Item {
-                node: hir::ItemConst(ref ty, body), ..
+                node: hir::ItemConst(_, body), ..
             })) |
             Some(hir_map::NodeImplItem(&hir::ImplItem {
-                node: hir::ImplItemKind::Const(ref ty, body), ..
+                node: hir::ImplItemKind::Const(_, body), ..
             })) => {
                 Some((&tcx.hir.body(body).value,
-                      tcx.tables.borrow().get(&def_id).cloned(),
-                      tcx.ast_ty_to_prim_ty(ty)))
+                      tcx.item_tables(def_id)))
             }
             Some(hir_map::NodeTraitItem(ti)) => match ti.node {
-                hir::TraitItemKind::Const(ref ty, default) => {
-                    if let Some(substs) = substs {
-                        // If we have a trait item and the substitutions for it,
-                        // `resolve_trait_associated_const` will select an impl
-                        // or the default.
-                        let trait_id = tcx.hir.get_parent(node_id);
-                        let trait_id = tcx.hir.local_def_id(trait_id);
-                        let default_value = default.map(|body| {
-                            (&tcx.hir.body(body).value,
-                             tcx.tables.borrow().get(&def_id).cloned(),
-                             tcx.ast_ty_to_prim_ty(ty))
-                        });
-                        resolve_trait_associated_const(tcx, def_id, default_value, trait_id, substs)
-                    } else {
-                        // Technically, without knowing anything about the
-                        // expression that generates the obligation, we could
-                        // still return the default if there is one. However,
-                        // it's safer to return `None` than to return some value
-                        // that may differ from what you would get from
-                        // correctly selecting an impl.
-                        None
-                    }
+                hir::TraitItemKind::Const(_, default) => {
+                    // If we have a trait item and the substitutions for it,
+                    // `resolve_trait_associated_const` will select an impl
+                    // or the default.
+                    let trait_id = tcx.hir.get_parent(node_id);
+                    let trait_id = tcx.hir.local_def_id(trait_id);
+                    let default_value = default.map(|body| {
+                        (&tcx.hir.body(body).value,
+                            tcx.item_tables(def_id))
+                    });
+                    resolve_trait_associated_const(tcx, def_id, default_value, trait_id, substs)
                 }
                 _ => None
             },
             Some(_) => None
         }
     } else {
-        let expr_tables_ty = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| {
-            (&body.value, Some(tcx.item_tables(def_id)),
-             Some(tcx.sess.cstore.item_type(tcx, def_id)))
+        let expr_and_tables = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| {
+            (&body.value, tcx.item_tables(def_id))
         });
         match tcx.sess.cstore.describe_def(def_id) {
             Some(Def::AssociatedConst(_)) => {
@@ -133,30 +123,26 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 // trait-associated const if the caller gives us the
                 // substitutions for the reference to it.
                 if let Some(trait_id) = trait_id {
-                    if let Some(substs) = substs {
-                        resolve_trait_associated_const(tcx, def_id, expr_tables_ty,
-                                                       trait_id, substs)
-                    } else {
-                        None
-                    }
+                    resolve_trait_associated_const(tcx, def_id, expr_and_tables,
+                                                   trait_id, substs)
                 } else {
-                    expr_tables_ty
+                    expr_and_tables
                 }
             },
-            Some(Def::Const(..)) => expr_tables_ty,
+            Some(Def::Const(..)) => expr_and_tables,
             _ => None
         }
     }
 }
 
 fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
-                                   -> Option<(&'tcx hir::Body, Option<&'a ty::TypeckTables<'tcx>>)>
+                                   -> Option<(&'tcx hir::Body, &'a ty::TypeckTables<'tcx>)>
 {
     if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
         FnLikeNode::from_node(tcx.hir.get(node_id)).and_then(|fn_like| {
             if fn_like.constness() == hir::Constness::Const {
                 Some((tcx.hir.body(fn_like.body()),
-                      tcx.tables.borrow().get(&def_id).cloned()))
+                      tcx.item_tables(def_id)))
             } else {
                 None
             }
@@ -164,7 +150,7 @@ fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
     } else {
         if tcx.sess.cstore.is_const_fn(def_id) {
             tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| {
-                (body, Some(tcx.item_tables(def_id)))
+                (body, tcx.item_tables(def_id))
             })
         } else {
             None
@@ -172,7 +158,7 @@ fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
     }
 }
 
-pub fn report_const_eval_err<'a, 'tcx>(
+fn build_const_eval_err<'a, 'tcx>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     err: &ConstEvalErr,
     primary_span: Span,
@@ -189,6 +175,18 @@ pub fn report_const_eval_err<'a, 'tcx>(
     diag
 }
 
+pub fn report_const_eval_err<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    err: &ConstEvalErr,
+    primary_span: Span,
+    primary_kind: &str)
+{
+    if let TypeckError = err.kind {
+        return;
+    }
+    build_const_eval_err(tcx, err, primary_span, primary_kind).emit();
+}
+
 pub fn fatal_const_eval_err<'a, 'tcx>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     err: &ConstEvalErr,
@@ -196,7 +194,7 @@ pub fn fatal_const_eval_err<'a, 'tcx>(
     primary_kind: &str)
     -> !
 {
-    report_const_eval_err(tcx, err, primary_span, primary_kind).emit();
+    report_const_eval_err(tcx, err, primary_span, primary_kind);
     tcx.sess.abort_if_errors();
     unreachable!()
 }
@@ -222,69 +220,58 @@ pub fn note_const_eval_err<'a, 'tcx>(
 
 pub struct ConstContext<'a, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    tables: Option<&'a ty::TypeckTables<'tcx>>,
-    fn_args: Option<DefIdMap<ConstVal>>
+    tables: &'a ty::TypeckTables<'tcx>,
+    substs: &'tcx Substs<'tcx>,
+    fn_args: Option<DefIdMap<ConstVal<'tcx>>>
 }
 
 impl<'a, 'tcx> ConstContext<'a, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, body: hir::BodyId) -> Self {
         let def_id = tcx.hir.body_owner_def_id(body);
-        ConstContext {
-            tcx: tcx,
-            tables: tcx.tables.borrow().get(&def_id).cloned(),
-            fn_args: None
-        }
+        ty::queries::mir_const_qualif::get(tcx, DUMMY_SP, def_id);
+        ConstContext::with_tables(tcx, tcx.item_tables(def_id))
     }
 
     pub fn with_tables(tcx: TyCtxt<'a, 'tcx, 'tcx>, tables: &'a ty::TypeckTables<'tcx>) -> Self {
         ConstContext {
             tcx: tcx,
-            tables: Some(tables),
+            tables: tables,
+            substs: tcx.intern_substs(&[]),
             fn_args: None
         }
     }
 
     /// Evaluate a constant expression in a context where the expression isn't
-    /// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked,
-    /// but a few places need to evaluate constants during type-checking, like
-    /// computing the length of an array. (See also the FIXME above EvalHint.)
-    pub fn eval(&self, e: &Expr, ty_hint: EvalHint<'tcx>) -> EvalResult {
-        eval_const_expr_partial(self, e, ty_hint)
+    /// guaranteed to be evaluatable.
+    pub fn eval(&self, e: &Expr) -> EvalResult<'tcx> {
+        if self.tables.tainted_by_errors {
+            signal!(e, TypeckError);
+        }
+        eval_const_expr_partial(self, e)
     }
 }
 
 #[derive(Clone, Debug)]
-pub struct ConstEvalErr {
+pub struct ConstEvalErr<'tcx> {
     pub span: Span,
-    pub kind: ErrKind,
+    pub kind: ErrKind<'tcx>,
 }
 
 #[derive(Clone, Debug)]
-pub enum ErrKind {
+pub enum ErrKind<'tcx> {
     CannotCast,
-    CannotCastTo(&'static str),
-    InvalidOpForInts(hir::BinOp_),
-    InvalidOpForBools(hir::BinOp_),
-    InvalidOpForFloats(hir::BinOp_),
-    InvalidOpForIntUint(hir::BinOp_),
-    InvalidOpForUintInt(hir::BinOp_),
-    NegateOn(ConstVal),
-    NotOn(ConstVal),
-    CallOn(ConstVal),
-
     MissingStructField,
+    NegateOn(ConstVal<'tcx>),
+    NotOn(ConstVal<'tcx>),
+    CallOn(ConstVal<'tcx>),
+
     NonConstPath,
     UnimplementedConstVal(&'static str),
-    UnresolvedPath,
     ExpectedConstTuple,
     ExpectedConstStruct,
-    TupleIndexOutOfBounds,
     IndexedNonVec,
-    IndexNegative,
-    IndexNotInt,
+    IndexNotUsize,
     IndexOutOfBounds { len: u64, index: u64 },
-    RepeatCountNotNatural,
-    RepeatCountNotInt,
 
     MiscBinaryOp,
     MiscCatchAll,
@@ -292,18 +279,17 @@ pub enum ErrKind {
     IndexOpFeatureGated,
     Math(ConstMathErr),
 
-    IntermediateUnsignedNegative,
-    /// Expected, Got
-    TypeMismatch(String, ConstInt),
+    ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>),
 
-    BadType(ConstVal),
-    ErroneousReferencedConstant(Box<ConstEvalErr>),
-    CharCast(ConstInt),
+    TypeckError
 }
 
-impl From<ConstMathErr> for ErrKind {
-    fn from(err: ConstMathErr) -> ErrKind {
-        Math(err)
+impl<'tcx> From<ConstMathErr> for ErrKind<'tcx> {
+    fn from(err: ConstMathErr) -> ErrKind<'tcx> {
+        match err {
+            ConstMathErr::UnsignedNegation => TypeckError,
+            _ => Math(err)
+        }
     }
 }
 
@@ -321,7 +307,7 @@ pub fn into_oneline(self) -> Cow<'a, str> {
     }
 }
 
-impl ConstEvalErr {
+impl<'tcx> ConstEvalErr<'tcx> {
     pub fn description(&self) -> ConstEvalErrDescription {
         use self::ErrKind::*;
         use self::ConstEvalErrDescription::*;
@@ -335,12 +321,6 @@ macro_rules! simple {
 
         match self.kind {
             CannotCast => simple!("can't cast this type"),
-            CannotCastTo(s) => simple!("can't cast this type to {}", s),
-            InvalidOpForInts(_) =>  simple!("can't do this op on integrals"),
-            InvalidOpForBools(_) =>  simple!("can't do this op on bools"),
-            InvalidOpForFloats(_) => simple!("can't do this op on floats"),
-            InvalidOpForIntUint(..) => simple!("can't do this op on an isize and usize"),
-            InvalidOpForUintInt(..) => simple!("can't do this op on a usize and isize"),
             NegateOn(ref const_val) => simple!("negate on {}", const_val.description()),
             NotOn(ref const_val) => simple!("not on {}", const_val.description()),
             CallOn(ref const_val) => simple!("call on {}", const_val.description()),
@@ -349,111 +329,42 @@ macro_rules! simple {
             NonConstPath        => simple!("non-constant path in constant expression"),
             UnimplementedConstVal(what) =>
                 simple!("unimplemented constant expression: {}", what),
-            UnresolvedPath => simple!("unresolved path in constant expression"),
             ExpectedConstTuple => simple!("expected constant tuple"),
             ExpectedConstStruct => simple!("expected constant struct"),
-            TupleIndexOutOfBounds => simple!("tuple index out of bounds"),
             IndexedNonVec => simple!("indexing is only supported for arrays"),
-            IndexNegative => simple!("indices must be non-negative integers"),
-            IndexNotInt => simple!("indices must be integers"),
+            IndexNotUsize => simple!("indices must be of type `usize`"),
             IndexOutOfBounds { len, index } => {
                 simple!("index out of bounds: the len is {} but the index is {}",
                         len, index)
             }
-            RepeatCountNotNatural => simple!("repeat count must be a natural number"),
-            RepeatCountNotInt => simple!("repeat count must be integers"),
 
             MiscBinaryOp => simple!("bad operands for binary"),
             MiscCatchAll => simple!("unsupported constant expr"),
             IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
             Math(ref err) => Simple(err.description().into_cow()),
 
-            IntermediateUnsignedNegative => simple!(
-                "during the computation of an unsigned a negative \
-                 number was encountered. This is most likely a bug in\
-                 the constant evaluator"),
-
-            TypeMismatch(ref expected, ref got) => {
-                simple!("expected {}, found {}", expected, got.description())
-            },
-            BadType(ref i) => simple!("value of wrong type: {:?}", i),
             ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),
-            CharCast(ref got) => {
-                simple!("only `u8` can be cast as `char`, not `{}`", got.description())
-            },
-        }
-    }
-}
 
-pub type EvalResult = Result<ConstVal, ConstEvalErr>;
-pub type CastResult = Result<ConstVal, ErrKind>;
-
-// FIXME: Long-term, this enum should go away: trying to evaluate
-// an expression which hasn't been type-checked is a recipe for
-// disaster.  That said, it's not clear how to fix ast_ty_to_ty
-// to avoid the ordering issue.
-
-/// Hint to determine how to evaluate constant expressions which
-/// might not be type-checked.
-#[derive(Copy, Clone, Debug)]
-pub enum EvalHint<'tcx> {
-    /// We have a type-checked expression.
-    ExprTypeChecked,
-    /// We have an expression which hasn't been type-checked, but we have
-    /// an idea of what the type will be because of the context. For example,
-    /// the length of an array is always `usize`. (This is referred to as
-    /// a hint because it isn't guaranteed to be consistent with what
-    /// type-checking would compute.)
-    UncheckedExprHint(Ty<'tcx>),
-    /// We have an expression which has not yet been type-checked, and
-    /// and we have no clue what the type will be.
-    UncheckedExprNoHint,
-}
-
-impl<'tcx> EvalHint<'tcx> {
-    fn erase_hint(&self) -> EvalHint<'tcx> {
-        match *self {
-            ExprTypeChecked => ExprTypeChecked,
-            UncheckedExprHint(_) | UncheckedExprNoHint => UncheckedExprNoHint,
-        }
-    }
-    fn checked_or(&self, ty: Ty<'tcx>) -> EvalHint<'tcx> {
-        match *self {
-            ExprTypeChecked => ExprTypeChecked,
-            _ => UncheckedExprHint(ty),
+            TypeckError => simple!("type-checking failed"),
         }
     }
 }
 
-macro_rules! signal {
-    ($e:expr, $exn:expr) => {
-        return Err(ConstEvalErr { span: $e.span, kind: $exn })
-    }
-}
+pub type EvalResult<'tcx> = Result<ConstVal<'tcx>, ConstEvalErr<'tcx>>;
+pub type CastResult<'tcx> = Result<ConstVal<'tcx>, ErrKind<'tcx>>;
 
 fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
-                                     e: &Expr,
-                                     ty_hint: EvalHint<'tcx>) -> EvalResult {
+                                     e: &Expr) -> EvalResult<'tcx> {
     let tcx = cx.tcx;
-    // Try to compute the type of the expression based on the EvalHint.
-    // (See also the definition of EvalHint, and the FIXME above EvalHint.)
-    let ety = match ty_hint {
-        ExprTypeChecked => {
-            // After type-checking, expr_ty is guaranteed to succeed.
-            cx.tables.map(|tables| tables.expr_ty(e))
-        }
-        UncheckedExprHint(ty) => {
-            // Use the type hint; it's not guaranteed to be right, but it's
-            // usually good enough.
-            Some(ty)
-        }
-        UncheckedExprNoHint => {
-            // This expression might not be type-checked, and we have no hint.
-            // Try to query the context for a type anyway; we might get lucky
-            // (for example, if the expression was imported from another crate).
-            cx.tables.and_then(|tables| tables.expr_ty_opt(e))
-        }
+    let ety = cx.tables.expr_ty(e);
+
+    // Avoid applying substitutions if they're empty, that'd ICE.
+    let ety = if cx.substs.is_empty() {
+        ety
+    } else {
+        ety.subst(tcx, cx.substs)
     };
+
     let result = match e.node {
       hir::ExprUnary(hir::UnNeg, ref inner) => {
         // unary neg literals already got their sign during creation
@@ -465,28 +376,28 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
             const I32_OVERFLOW: u128 = i32::min_value() as u32 as u128;
             const I64_OVERFLOW: u128 = i64::min_value() as u64 as u128;
             const I128_OVERFLOW: u128 = i128::min_value() as u128;
-            match (&lit.node, ety.map(|t| &t.sty)) {
-                (&LitKind::Int(I8_OVERFLOW, _), Some(&ty::TyInt(IntTy::I8))) |
+            match (&lit.node, &ety.sty) {
+                (&LitKind::Int(I8_OVERFLOW, _), &ty::TyInt(IntTy::I8)) |
                 (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => {
                     return Ok(Integral(I8(i8::min_value())))
                 },
-                (&LitKind::Int(I16_OVERFLOW, _), Some(&ty::TyInt(IntTy::I16))) |
+                (&LitKind::Int(I16_OVERFLOW, _), &ty::TyInt(IntTy::I16)) |
                 (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => {
                     return Ok(Integral(I16(i16::min_value())))
                 },
-                (&LitKind::Int(I32_OVERFLOW, _), Some(&ty::TyInt(IntTy::I32))) |
+                (&LitKind::Int(I32_OVERFLOW, _), &ty::TyInt(IntTy::I32)) |
                 (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => {
                     return Ok(Integral(I32(i32::min_value())))
                 },
-                (&LitKind::Int(I64_OVERFLOW, _), Some(&ty::TyInt(IntTy::I64))) |
+                (&LitKind::Int(I64_OVERFLOW, _), &ty::TyInt(IntTy::I64)) |
                 (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => {
                     return Ok(Integral(I64(i64::min_value())))
                 },
-                (&LitKind::Int(I128_OVERFLOW, _), Some(&ty::TyInt(IntTy::I128))) |
+                (&LitKind::Int(I128_OVERFLOW, _), &ty::TyInt(IntTy::I128)) |
                 (&LitKind::Int(I128_OVERFLOW, Signed(IntTy::I128)), _) => {
                     return Ok(Integral(I128(i128::min_value())))
                 },
-                (&LitKind::Int(n, _), Some(&ty::TyInt(IntTy::Is))) |
+                (&LitKind::Int(n, _), &ty::TyInt(IntTy::Is)) |
                 (&LitKind::Int(n, Signed(IntTy::Is)), _) => {
                     match tcx.sess.target.int_type {
                         IntTy::I16 => if n == I16_OVERFLOW {
@@ -498,20 +409,20 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
                         IntTy::I64 => if n == I64_OVERFLOW {
                             return Ok(Integral(Isize(Is64(i64::min_value()))));
                         },
-                        _ => bug!(),
+                        _ => span_bug!(e.span, "typeck error")
                     }
                 },
                 _ => {},
             }
         }
-        match cx.eval(inner, ty_hint)? {
+        match cx.eval(inner)? {
           Float(f) => Float(-f),
           Integral(i) => Integral(math!(e, -i)),
           const_val => signal!(e, NegateOn(const_val)),
         }
       }
       hir::ExprUnary(hir::UnNot, ref inner) => {
-        match cx.eval(inner, ty_hint)? {
+        match cx.eval(inner)? {
           Integral(i) => Integral(math!(e, !i)),
           Bool(b) => Bool(!b),
           const_val => signal!(e, NotOn(const_val)),
@@ -519,16 +430,11 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
       }
       hir::ExprUnary(hir::UnDeref, _) => signal!(e, UnimplementedConstVal("deref operation")),
       hir::ExprBinary(op, ref a, ref b) => {
-        let b_ty = match op.node {
-            hir::BiShl | hir::BiShr => ty_hint.erase_hint(),
-            _ => ty_hint
-        };
         // technically, if we don't have type hints, but integral eval
         // gives us a type through a type-suffix, cast or const def type
         // we need to re-eval the other value of the BinOp if it was
         // not inferred
-        match (cx.eval(a, ty_hint)?,
-               cx.eval(b, b_ty)?) {
+        match (cx.eval(a)?, cx.eval(b)?) {
           (Float(a), Float(b)) => {
             use std::cmp::Ordering::*;
             match op.node {
@@ -543,7 +449,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
               hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
               hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
               hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
-              _ => signal!(e, InvalidOpForFloats(op.node)),
+              _ => span_bug!(e.span, "typeck error"),
             }
           }
           (Integral(a), Integral(b)) => {
@@ -565,7 +471,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
               hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
               hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
               hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
-              _ => signal!(e, InvalidOpForInts(op.node)),
+              _ => span_bug!(e.span, "typeck error"),
             }
           }
           (Bool(a), Bool(b)) => {
@@ -581,90 +487,68 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
               hir::BiLe => a <= b,
               hir::BiGe => a >= b,
               hir::BiGt => a > b,
-              _ => signal!(e, InvalidOpForBools(op.node)),
+              _ => span_bug!(e.span, "typeck error"),
+             })
+          }
+          (Char(a), Char(b)) => {
+            Bool(match op.node {
+              hir::BiEq => a == b,
+              hir::BiNe => a != b,
+              hir::BiLt => a < b,
+              hir::BiLe => a <= b,
+              hir::BiGe => a >= b,
+              hir::BiGt => a > b,
+              _ => span_bug!(e.span, "typeck error"),
              })
           }
 
           _ => signal!(e, MiscBinaryOp),
         }
       }
-      hir::ExprCast(ref base, ref target_ty) => {
-        let ety = tcx.ast_ty_to_prim_ty(&target_ty).or(ety)
-                .unwrap_or_else(|| {
-                    tcx.sess.span_fatal(target_ty.span,
-                                        "target type not found for const cast")
-                });
-
-        let base_hint = if let ExprTypeChecked = ty_hint {
-            ExprTypeChecked
-        } else {
-            match cx.tables.and_then(|tables| tables.expr_ty_opt(&base)) {
-                Some(t) => UncheckedExprHint(t),
-                None => ty_hint
-            }
-        };
-
-        let val = match cx.eval(base, base_hint) {
-            Ok(val) => val,
-            Err(ConstEvalErr { kind: ErroneousReferencedConstant(
-                box ConstEvalErr { kind: TypeMismatch(_, val), .. }), .. }) |
-            Err(ConstEvalErr { kind: TypeMismatch(_, val), .. }) => {
-                // Something like `5i8 as usize` doesn't need a type hint for the base
-                // instead take the type hint from the inner value
-                let hint = match val.int_type() {
-                    Some(IntType::UnsignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_uint(ty)),
-                    Some(IntType::SignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_int(ty)),
-                    // we had a type hint, so we can't have an unknown type
-                    None => bug!(),
-                };
-                cx.eval(base, hint)?
-            },
-            Err(e) => return Err(e),
-        };
-        match cast_const(tcx, val, ety) {
+      hir::ExprCast(ref base, _) => {
+        match cast_const(tcx, cx.eval(base)?, ety) {
             Ok(val) => val,
             Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
         }
       }
       hir::ExprPath(ref qpath) => {
-          let def = cx.tables.map(|tables| tables.qpath_def(qpath, e.id)).unwrap_or_else(|| {
-            // There are no tables so we can only handle already-resolved HIR.
-            match *qpath {
-                hir::QPath::Resolved(_, ref path) => path.def,
-                hir::QPath::TypeRelative(..) => Def::Err
-            }
-          });
-          match def {
+        let substs = cx.tables.node_id_item_substs(e.id)
+            .unwrap_or_else(|| tcx.intern_substs(&[]));
+
+        // Avoid applying substitutions if they're empty, that'd ICE.
+        let substs = if cx.substs.is_empty() {
+            substs
+        } else {
+            substs.subst(tcx, cx.substs)
+        };
+
+          match cx.tables.qpath_def(qpath, e.id) {
               Def::Const(def_id) |
               Def::AssociatedConst(def_id) => {
-                  let substs = if let ExprTypeChecked = ty_hint {
-                      Some(cx.tables.and_then(|tables| tables.node_id_item_substs(e.id))
-                        .unwrap_or_else(|| tcx.intern_substs(&[])))
-                  } else {
-                      None
-                  };
-                  if let Some((expr, tables, ty)) = lookup_const_by_id(tcx, def_id, substs) {
-                      let item_hint = match ty {
-                          Some(ty) => ty_hint.checked_or(ty),
-                          None => ty_hint,
-                      };
-                      let cx = ConstContext { tcx: tcx, tables: tables, fn_args: None };
-                      match cx.eval(expr, item_hint) {
+                  if let Some((expr, tables)) = lookup_const_by_id(tcx, def_id, substs) {
+                      let cx = ConstContext::with_tables(tcx, tables);
+                      match cx.eval(expr) {
                           Ok(val) => val,
+                          Err(ConstEvalErr { kind: TypeckError, .. }) => {
+                              signal!(e, TypeckError);
+                          }
                           Err(err) => {
                               debug!("bad reference: {:?}, {:?}", err.description(), err.span);
                               signal!(e, ErroneousReferencedConstant(box err))
                           },
                       }
                   } else {
-                      signal!(e, NonConstPath);
+                      signal!(e, TypeckError);
                   }
               },
               Def::VariantCtor(variant_def, ..) => {
                   if let Some((expr, tables)) = lookup_variant_by_id(tcx, variant_def) {
-                      let cx = ConstContext { tcx: tcx, tables: tables, fn_args: None };
-                      match cx.eval(expr, ty_hint) {
+                      let cx = ConstContext::with_tables(tcx, tables);
+                      match cx.eval(expr) {
                           Ok(val) => val,
+                          Err(ConstEvalErr { kind: TypeckError, .. }) => {
+                              signal!(e, TypeckError);
+                          }
                           Err(err) => {
                               debug!("bad reference: {:?}, {:?}", err.description(), err.span);
                               signal!(e, ErroneousReferencedConstant(box err))
@@ -685,16 +569,14 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
                       signal!(e, NonConstPath);
                   }
               },
-              Def::Method(id) | Def::Fn(id) => Function(id),
-              Def::Err => signal!(e, UnresolvedPath),
+              Def::Method(id) | Def::Fn(id) => Function(id, substs),
+              Def::Err => span_bug!(e.span, "typeck error"),
               _ => signal!(e, NonConstPath),
           }
       }
       hir::ExprCall(ref callee, ref args) => {
-          let sub_ty_hint = ty_hint.erase_hint();
-          let callee_val = cx.eval(callee, sub_ty_hint)?;
-          let did = match callee_val {
-              Function(did) => did,
+          let (did, substs) = match cx.eval(callee)? {
+              Function(did, substs) => (did, substs),
               Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")),
               callee => signal!(e, CallOn(callee)),
           };
@@ -711,8 +593,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
 
           let mut call_args = DefIdMap();
           for (arg, arg_expr) in arg_defs.into_iter().zip(args.iter()) {
-              let arg_hint = ty_hint.erase_hint();
-              let arg_val = cx.eval(arg_expr, arg_hint)?;
+              let arg_val = cx.eval(arg_expr)?;
               debug!("const call arg: {:?}", arg);
               if let Some(def_id) = arg {
                 assert!(call_args.insert(def_id, arg_val).is_none());
@@ -722,9 +603,10 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
           let callee_cx = ConstContext {
             tcx: tcx,
             tables: tables,
+            substs: substs,
             fn_args: Some(call_args)
           };
-          callee_cx.eval(&body.value, ty_hint)?
+          callee_cx.eval(&body.value)?
       },
       hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ety) {
           Ok(val) => val,
@@ -732,32 +614,27 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
       },
       hir::ExprBlock(ref block) => {
         match block.expr {
-            Some(ref expr) => cx.eval(expr, ty_hint)?,
-            None => signal!(e, UnimplementedConstVal("empty block")),
+            Some(ref expr) => cx.eval(expr)?,
+            None => Tuple(vec![]),
         }
       }
-      hir::ExprType(ref e, _) => cx.eval(e, ty_hint)?,
+      hir::ExprType(ref e, _) => cx.eval(e)?,
       hir::ExprTup(ref fields) => {
-        let field_hint = ty_hint.erase_hint();
-        Tuple(fields.iter().map(|e| cx.eval(e, field_hint)).collect::<Result<_, _>>()?)
+        Tuple(fields.iter().map(|e| cx.eval(e)).collect::<Result<_, _>>()?)
       }
       hir::ExprStruct(_, ref fields, _) => {
-        let field_hint = ty_hint.erase_hint();
         Struct(fields.iter().map(|f| {
-            cx.eval(&f.expr, field_hint).map(|v| (f.name.node, v))
+            cx.eval(&f.expr).map(|v| (f.name.node, v))
         }).collect::<Result<_, _>>()?)
       }
       hir::ExprIndex(ref arr, ref idx) => {
         if !tcx.sess.features.borrow().const_indexing {
             signal!(e, IndexOpFeatureGated);
         }
-        let arr_hint = ty_hint.erase_hint();
-        let arr = cx.eval(arr, arr_hint)?;
-        let idx_hint = ty_hint.checked_or(tcx.types.usize);
-        let idx = match cx.eval(idx, idx_hint)? {
+        let arr = cx.eval(arr)?;
+        let idx = match cx.eval(idx)? {
             Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type),
-            Integral(_) => bug!(),
-            _ => signal!(idx, IndexNotInt),
+            _ => signal!(idx, IndexNotUsize),
         };
         assert_eq!(idx as usize as u64, idx);
         match arr {
@@ -787,45 +664,25 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
         }
       }
       hir::ExprArray(ref v) => {
-        let elem_hint = ty_hint.erase_hint();
-        Array(v.iter().map(|e| cx.eval(e, elem_hint)).collect::<Result<_, _>>()?)
+        Array(v.iter().map(|e| cx.eval(e)).collect::<Result<_, _>>()?)
       }
-      hir::ExprRepeat(ref elem, count) => {
-          let elem_hint = ty_hint.erase_hint();
-          let len_hint = ty_hint.checked_or(tcx.types.usize);
-          let n = if let Some(ty) = ety {
-            // For cross-crate constants, we have the type already,
-            // but not the body for `count`, so use the type.
-            match ty.sty {
-                ty::TyArray(_, n) => n as u64,
-                _ => bug!()
-            }
-          } else {
-            let n = &tcx.hir.body(count).value;
-            match ConstContext::new(tcx, count).eval(n, len_hint)? {
-                Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type),
-                Integral(_) => signal!(e, RepeatCountNotNatural),
-                _ => signal!(e, RepeatCountNotInt),
-            }
+      hir::ExprRepeat(ref elem, _) => {
+          let n = match ety.sty {
+            ty::TyArray(_, n) => n as u64,
+            _ => span_bug!(e.span, "typeck error")
           };
-          Repeat(Box::new(cx.eval(elem, elem_hint)?), n)
+          Repeat(Box::new(cx.eval(elem)?), n)
       },
       hir::ExprTupField(ref base, index) => {
-        let base_hint = ty_hint.erase_hint();
-        let c = cx.eval(base, base_hint)?;
+        let c = cx.eval(base)?;
         if let Tuple(ref fields) = c {
-            if let Some(elem) = fields.get(index.node) {
-                elem.clone()
-            } else {
-                signal!(e, TupleIndexOutOfBounds);
-            }
+            fields[index.node].clone()
         } else {
             signal!(base, ExpectedConstTuple);
         }
       }
       hir::ExprField(ref base, field_name) => {
-        let base_hint = ty_hint.erase_hint();
-        let c = cx.eval(base, base_hint)?;
+        let c = cx.eval(base)?;
         if let Struct(ref fields) = c {
             if let Some(f) = fields.get(&field_name.node) {
                 f.clone()
@@ -840,90 +697,23 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
       _ => signal!(e, MiscCatchAll)
     };
 
-    match (ety.map(|t| &t.sty), result) {
-        (Some(ref ty_hint), Integral(i)) => match infer(i, tcx, ty_hint) {
-            Ok(inferred) => Ok(Integral(inferred)),
-            Err(err) => signal!(e, err),
-        },
-        (_, result) => Ok(result),
-    }
-}
-
-fn infer<'a, 'tcx>(i: ConstInt,
-                   tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                   ty_hint: &ty::TypeVariants<'tcx>)
-                   -> Result<ConstInt, ErrKind> {
-    use syntax::ast::*;
-
-    match (ty_hint, i) {
-        (&ty::TyInt(IntTy::I8), result @ I8(_)) => Ok(result),
-        (&ty::TyInt(IntTy::I16), result @ I16(_)) => Ok(result),
-        (&ty::TyInt(IntTy::I32), result @ I32(_)) => Ok(result),
-        (&ty::TyInt(IntTy::I64), result @ I64(_)) => Ok(result),
-        (&ty::TyInt(IntTy::I128), result @ I128(_)) => Ok(result),
-        (&ty::TyInt(IntTy::Is), result @ Isize(_)) => Ok(result),
-
-        (&ty::TyUint(UintTy::U8), result @ U8(_)) => Ok(result),
-        (&ty::TyUint(UintTy::U16), result @ U16(_)) => Ok(result),
-        (&ty::TyUint(UintTy::U32), result @ U32(_)) => Ok(result),
-        (&ty::TyUint(UintTy::U64), result @ U64(_)) => Ok(result),
-        (&ty::TyUint(UintTy::U128), result @ U128(_)) => Ok(result),
-        (&ty::TyUint(UintTy::Us), result @ Usize(_)) => Ok(result),
-
-        (&ty::TyInt(IntTy::I8), Infer(i)) => Ok(I8(i as i128 as i8)),
-        (&ty::TyInt(IntTy::I16), Infer(i)) => Ok(I16(i as i128 as i16)),
-        (&ty::TyInt(IntTy::I32), Infer(i)) => Ok(I32(i as i128 as i32)),
-        (&ty::TyInt(IntTy::I64), Infer(i)) => Ok(I64(i as i128 as i64)),
-        (&ty::TyInt(IntTy::I128), Infer(i)) => Ok(I128(i as i128)),
-        (&ty::TyInt(IntTy::Is), Infer(i)) => {
-            Ok(Isize(ConstIsize::new_truncating(i as i128, tcx.sess.target.int_type)))
-        },
-
-        (&ty::TyInt(IntTy::I8), InferSigned(i)) => Ok(I8(i as i8)),
-        (&ty::TyInt(IntTy::I16), InferSigned(i)) => Ok(I16(i as i16)),
-        (&ty::TyInt(IntTy::I32), InferSigned(i)) => Ok(I32(i as i32)),
-        (&ty::TyInt(IntTy::I64), InferSigned(i)) => Ok(I64(i as i64)),
-        (&ty::TyInt(IntTy::I128), InferSigned(i)) => Ok(I128(i)),
-        (&ty::TyInt(IntTy::Is), InferSigned(i)) => {
-            Ok(Isize(ConstIsize::new_truncating(i, tcx.sess.target.int_type)))
-        },
-
-        (&ty::TyUint(UintTy::U8), Infer(i)) => Ok(U8(i as u8)),
-        (&ty::TyUint(UintTy::U16), Infer(i)) => Ok(U16(i as u16)),
-        (&ty::TyUint(UintTy::U32), Infer(i)) => Ok(U32(i as u32)),
-        (&ty::TyUint(UintTy::U64), Infer(i)) => Ok(U64(i as u64)),
-        (&ty::TyUint(UintTy::U128), Infer(i)) => Ok(U128(i)),
-        (&ty::TyUint(UintTy::Us), Infer(i)) => {
-            Ok(Usize(ConstUsize::new_truncating(i, tcx.sess.target.uint_type)))
-        },
-        (&ty::TyUint(_), InferSigned(_)) => Err(IntermediateUnsignedNegative),
-
-        (&ty::TyInt(ity), i) => Err(TypeMismatch(ity.to_string(), i)),
-        (&ty::TyUint(ity), i) => Err(TypeMismatch(ity.to_string(), i)),
-
-        (&ty::TyAdt(adt, _), i) if adt.is_enum() => {
-            let hints = tcx.lookup_repr_hints(adt.did);
-            let int_ty = tcx.enum_repr_type(hints.iter().next());
-            infer(i, tcx, &int_ty.to_ty(tcx).sty)
-        },
-        (_, i) => Err(BadType(ConstVal::Integral(i))),
-    }
+    Ok(result)
 }
 
 fn resolve_trait_associated_const<'a, 'tcx: 'a>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     trait_item_id: DefId,
-    default_value: Option<(&'tcx Expr, Option<&'a ty::TypeckTables<'tcx>>, Option<ty::Ty<'tcx>>)>,
+    default_value: Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)>,
     trait_id: DefId,
     rcvr_substs: &'tcx Substs<'tcx>
-) -> Option<(&'tcx Expr, Option<&'a ty::TypeckTables<'tcx>>, Option<ty::Ty<'tcx>>)>
+) -> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)>
 {
     let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, rcvr_substs));
     debug!("resolve_trait_associated_const: trait_ref={:?}",
            trait_ref);
 
     tcx.populate_implementations_for_trait_if_necessary(trait_id);
-    tcx.infer_ctxt((), Reveal::NotSpecializable).enter(|infcx| {
+    tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
         let mut selcx = traits::SelectionContext::new(&infcx);
         let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
                                                  trait_ref.to_poly_trait_predicate());
@@ -951,7 +741,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(
                 let ac = tcx.associated_items(impl_data.impl_def_id)
                     .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
                 match ac {
-                    Some(ic) => lookup_const_by_id(tcx, ic.def_id, None),
+                    Some(ic) => lookup_const_by_id(tcx, ic.def_id, Substs::empty()),
                     None => default_value,
                 }
             }
@@ -962,7 +752,10 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(
     })
 }
 
-fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::Ty) -> CastResult {
+fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                            val: ConstInt,
+                            ty: Ty<'tcx>)
+                            -> CastResult<'tcx> {
     let v = val.to_u128_unchecked();
     match ty.sty {
         ty::TyBool if v == 0 => Ok(Bool(false)),
@@ -983,42 +776,31 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::
         ty::TyUint(ast::UintTy::Us) => {
             Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type))))
         },
-        ty::TyFloat(ast::FloatTy::F64) => match val.erase_type() {
-            Infer(u) => Ok(Float(F64(u as f64))),
-            InferSigned(i) => Ok(Float(F64(i as f64))),
-            _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"),
-        },
-        ty::TyFloat(ast::FloatTy::F32) => match val.erase_type() {
-            Infer(u) => Ok(Float(F32(u as f32))),
-            InferSigned(i) => Ok(Float(F32(i as f32))),
-            _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"),
-        },
+        ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(val.to_f64()))),
+        ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(val.to_f32()))),
         ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
-        ty::TyChar => match infer(val, tcx, &ty::TyUint(ast::UintTy::U8)) {
-            Ok(U8(u)) => Ok(Char(u as char)),
-            // can only occur before typeck, typeck blocks `T as char` for `T` != `u8`
-            _ => Err(CharCast(val)),
+        ty::TyChar => match val {
+            U8(u) => Ok(Char(u as char)),
+            _ => bug!(),
         },
-        _ => Err(CannotCast),
+        _ => bug!(),
     }
 }
 
 fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                               val: ConstFloat,
-                              ty: ty::Ty) -> CastResult {
+                              ty: Ty<'tcx>) -> CastResult<'tcx> {
     match ty.sty {
         ty::TyInt(_) | ty::TyUint(_) => {
             let i = match val {
-                F32(f) if f >= 0.0 => Infer(f as u128),
-                FInfer { f64: f, .. } |
-                F64(f) if f >= 0.0 => Infer(f as u128),
+                F32(f) if f >= 0.0 => U128(f as u128),
+                F64(f) if f >= 0.0 => U128(f as u128),
 
-                F32(f) => InferSigned(f as i128),
-                FInfer { f64: f, .. } |
-                F64(f) => InferSigned(f as i128)
+                F32(f) => I128(f as i128),
+                F64(f) => I128(f as i128)
             };
 
-            if let (InferSigned(_), &ty::TyUint(_)) = (i, &ty.sty) {
+            if let (I128(_), &ty::TyUint(_)) = (i, &ty.sty) {
                 return Err(CannotCast);
             }
 
@@ -1026,23 +808,26 @@ fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
         ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val {
             F32(f) => f as f64,
-            FInfer { f64: f, .. } | F64(f) => f
+            F64(f) => f
         }))),
         ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val {
             F64(f) => f as f32,
-            FInfer { f32: f, .. } | F32(f) => f
+            F32(f) => f
         }))),
         _ => Err(CannotCast),
     }
 }
 
-fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstVal, ty: ty::Ty) -> CastResult {
+fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                        val: ConstVal<'tcx>,
+                        ty: Ty<'tcx>)
+                        -> CastResult<'tcx> {
     match val {
         Integral(i) => cast_const_int(tcx, i, ty),
-        Bool(b) => cast_const_int(tcx, Infer(b as u128), ty),
+        Bool(b) => cast_const_int(tcx, U8(b as u8), ty),
         Float(f) => cast_const_float(tcx, f, ty),
-        Char(c) => cast_const_int(tcx, Infer(c as u128), ty),
-        Function(_) => Err(UnimplementedConstVal("casting fn pointers")),
+        Char(c) => cast_const_int(tcx, U32(c as u32), ty),
+        Function(..) => Err(UnimplementedConstVal("casting fn pointers")),
         ByteStr(b) => match ty.sty {
             ty::TyRawPtr(_) => {
                 Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr"))
@@ -1070,67 +855,56 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstVal, ty: ty::Ty)
 
 fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind,
                           tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          ty_hint: Option<Ty<'tcx>>)
-                          -> Result<ConstVal, ErrKind> {
+                          mut ty: Ty<'tcx>)
+                          -> Result<ConstVal<'tcx>, ErrKind<'tcx>> {
     use syntax::ast::*;
     use syntax::ast::LitIntType::*;
+
+    if let ty::TyAdt(adt, _) = ty.sty {
+        if adt.is_enum() {
+            ty = adt.repr.discr_type().to_ty(tcx)
+        }
+    }
+
     match *lit {
         LitKind::Str(ref s, _) => Ok(Str(s.as_str())),
         LitKind::ByteStr(ref data) => Ok(ByteStr(data.clone())),
         LitKind::Byte(n) => Ok(Integral(U8(n))),
-        LitKind::Int(n, Signed(ity)) => {
-            infer(InferSigned(n as i128), tcx, &ty::TyInt(ity)).map(Integral)
-        },
-
-        // FIXME: this should become u128.
-        LitKind::Int(n, Unsuffixed) => {
-            match ty_hint.map(|t| &t.sty) {
-                Some(&ty::TyInt(ity)) => {
-                    infer(InferSigned(n as i128), tcx, &ty::TyInt(ity)).map(Integral)
-                },
-                Some(&ty::TyUint(uty)) => {
-                    infer(Infer(n as u128), tcx, &ty::TyUint(uty)).map(Integral)
-                },
-                None => Ok(Integral(Infer(n as u128))),
-                Some(&ty::TyAdt(adt, _)) => {
-                    let hints = tcx.lookup_repr_hints(adt.did);
-                    let int_ty = tcx.enum_repr_type(hints.iter().next());
-                    infer(Infer(n as u128), tcx, &int_ty.to_ty(tcx).sty).map(Integral)
-                },
-                Some(ty_hint) => bug!("bad ty_hint: {:?}, {:?}", ty_hint, lit),
+        LitKind::Int(n, hint) => {
+            match (&ty.sty, hint) {
+                (&ty::TyInt(ity), _) |
+                (_, Signed(ity)) => {
+                    Ok(Integral(ConstInt::new_signed_truncating(n as i128,
+                        ity, tcx.sess.target.int_type)))
+                }
+                (&ty::TyUint(uty), _) |
+                (_, Unsigned(uty)) => {
+                    Ok(Integral(ConstInt::new_unsigned_truncating(n as u128,
+                        uty, tcx.sess.target.uint_type)))
+                }
+                _ => bug!()
             }
-        },
-        LitKind::Int(n, Unsigned(ity)) => {
-            infer(Infer(n as u128), tcx, &ty::TyUint(ity)).map(Integral)
-        },
-
+        }
         LitKind::Float(n, fty) => {
-            parse_float(&n.as_str(), Some(fty)).map(Float)
+            parse_float(&n.as_str(), fty).map(Float)
         }
         LitKind::FloatUnsuffixed(n) => {
-            let fty_hint = match ty_hint.map(|t| &t.sty) {
-                Some(&ty::TyFloat(fty)) => Some(fty),
-                _ => None
+            let fty = match ty.sty {
+                ty::TyFloat(fty) => fty,
+                _ => bug!()
             };
-            parse_float(&n.as_str(), fty_hint).map(Float)
+            parse_float(&n.as_str(), fty).map(Float)
         }
         LitKind::Bool(b) => Ok(Bool(b)),
         LitKind::Char(c) => Ok(Char(c)),
     }
 }
 
-fn parse_float(num: &str, fty_hint: Option<ast::FloatTy>)
-               -> Result<ConstFloat, ErrKind> {
-    let val = match fty_hint {
-        Some(ast::FloatTy::F32) => num.parse::<f32>().map(F32),
-        Some(ast::FloatTy::F64) => num.parse::<f64>().map(F64),
-        None => {
-            num.parse::<f32>().and_then(|f32| {
-                num.parse::<f64>().map(|f64| {
-                    FInfer { f32: f32, f64: f64 }
-                })
-            })
-        }
+fn parse_float<'tcx>(num: &str, fty: ast::FloatTy)
+                     -> Result<ConstFloat, ErrKind<'tcx>> {
+    let val = match fty {
+        ast::FloatTy::F32 => num.parse::<f32>().map(F32),
+        ast::FloatTy::F64 => num.parse::<f64>().map(F64)
     };
     val.map_err(|_| {
         // FIXME(#31407) this is only necessary because float parsing is buggy
@@ -1170,17 +944,17 @@ pub fn compare_lit_exprs(&self,
                              a: &Expr,
                              b: &Expr) -> Result<Ordering, ErrorReported> {
         let tcx = self.tcx;
-        let a = match self.eval(a, ExprTypeChecked) {
+        let a = match self.eval(a) {
             Ok(a) => a,
             Err(e) => {
-                report_const_eval_err(tcx, &e, a.span, "expression").emit();
+                report_const_eval_err(tcx, &e, a.span, "expression");
                 return Err(ErrorReported);
             }
         };
-        let b = match self.eval(b, ExprTypeChecked) {
+        let b = match self.eval(b) {
             Ok(b) => b,
             Err(e) => {
-                report_const_eval_err(tcx, &e, b.span, "expression").emit();
+                report_const_eval_err(tcx, &e, b.span, "expression");
                 return Err(ErrorReported);
             }
         };
@@ -1195,26 +969,17 @@ pub fn eval_length<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              reason: &str)
                              -> Result<usize, ErrorReported>
 {
-    let hint = UncheckedExprHint(tcx.types.usize);
     let count_expr = &tcx.hir.body(count).value;
-    match ConstContext::new(tcx, count).eval(count_expr, hint) {
+    match ConstContext::new(tcx, count).eval(count_expr) {
         Ok(Integral(Usize(count))) => {
             let val = count.as_u64(tcx.sess.target.uint_type);
             assert_eq!(val as usize as u64, val);
             Ok(val as usize)
         },
-        Ok(const_val) => {
-            struct_span_err!(tcx.sess, count_expr.span, E0306,
-                             "expected `usize` for {}, found {}",
-                             reason,
-                             const_val.description())
-                .span_label(count_expr.span, &format!("expected `usize`"))
-                .emit();
-
-            Err(ErrorReported)
-        }
+        Ok(_) |
+        Err(ConstEvalErr { kind: TypeckError, .. }) => Err(ErrorReported),
         Err(err) => {
-            let mut diag = report_const_eval_err(
+            let mut diag = build_const_eval_err(
                 tcx, &err, count_expr.span, reason);
 
             if let hir::ExprPath(hir::QPath::Resolved(None, ref path)) = count_expr.node {