Code change:
let result = match e.node { hir::ExprUnary(hir::UnNeg, ref inner) => { // unary neg literals already got their sign during creation <del> match inner.node { hir::ExprLit(ref lit) => { use syntax::ast::*; use syntax::ast::LitIntType::*; const I8_OVERFLOW: u64 = ::std::i8::MAX as u64 + 1; const I16_OVERFLOW: u64 = ::std::i16::MAX as u64 + 1; const I32_OVERFLOW: u64 = ::std::i32::MAX as u64 + 1; const I64_OVERFLOW: u64 = ::std::i64::MAX as u64 + 1; match (&lit.node, ety.map(|t| &t.sty)) { (&LitKind::Int(I8_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I8))) | (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { return Ok(Integral(I8(::std::i8::MIN))) }, (&LitKind::Int(I16_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I16))) | (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { return Ok(Integral(I16(::std::i16::MIN))) }, (&LitKind::Int(I32_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I32))) | (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { return Ok(Integral(I32(::std::i32::MIN))) }, (&LitKind::Int(I64_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I64))) | (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { return Ok(Integral(I64(::std::i64::MIN))) }, (&LitKind::Int(n, Unsuffixed), Some(&ty::TyInt(IntTy::Is))) | (&LitKind::Int(n, Signed(IntTy::Is)), _) => { match tcx.sess.target.int_type { IntTy::I16 => if n == I16_OVERFLOW { return Ok(Integral(Isize(Is16(::std::i16::MIN)))); }, IntTy::I32 => if n == I32_OVERFLOW { return Ok(Integral(Isize(Is32(::std::i32::MIN)))); }, IntTy::I64 => if n == I64_OVERFLOW { return Ok(Integral(Isize(Is64(::std::i64::MIN)))); }, _ => bug!(), } }, _ => {}, } }, hir::ExprUnary(hir::UnNeg, ref inner) => { // skip `--$expr` return eval_const_expr_partial(tcx, inner, ty_hint, fn_args); }, _ => {}, </del> <ins> if let hir::ExprLit(ref lit) = inner.node { use syntax::ast::*; use syntax::ast::LitIntType::*; const I8_OVERFLOW: u64 = ::std::i8::MAX as u64 + 1; const I16_OVERFLOW: u64 = ::std::i16::MAX as u64 + 1; const I32_OVERFLOW: u64 = ::std::i32::MAX as u64 + 1; const I64_OVERFLOW: u64 = ::std::i64::MAX as u64 + 1; match (&lit.node, ety.map(|t| &t.sty)) { (&LitKind::Int(I8_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I8))) | (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { return Ok(Integral(I8(::std::i8::MIN))) }, (&LitKind::Int(I16_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I16))) | (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { return Ok(Integral(I16(::std::i16::MIN))) }, (&LitKind::Int(I32_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I32))) | (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { return Ok(Integral(I32(::std::i32::MIN))) }, (&LitKind::Int(I64_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I64))) | (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { return Ok(Integral(I64(::std::i64::MIN))) }, (&LitKind::Int(n, Unsuffixed), Some(&ty::TyInt(IntTy::Is))) | (&LitKind::Int(n, Signed(IntTy::Is)), _) => { match tcx.sess.target.int_type { IntTy::I16 => if n == I16_OVERFLOW { return Ok(Integral(Isize(Is16(::std::i16::MIN)))); }, IntTy::I32 => if n == I32_OVERFLOW { return Ok(Integral(Isize(Is32(::std::i32::MIN)))); }, IntTy::I64 => if n == I64_OVERFLOW { return Ok(Integral(Isize(Is64(::std::i64::MIN)))); }, _ => bug!(), } }, _ => {}, } </ins> } match eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)? { Float(f) => Float(-f),

Related discussion:
text: This is clearly ridiculous, but rustc is perfectly happy with it. Regression from 1.9.
prints . Using the constant in any other way, e.g. errors out:
cc is that fallout from your new const eval?
I haven't removed anything, not that I can think of, to cause this.
Then it's very likely fallout from (skipping double negations)
The remaining error in that testcase feels wrong. should, IMO, be - the literal is in range, but the negation overflows.
Actually I think it should be valid code, but yield , at least that's what the original literal out of range lints do
But overflows when negating, which is an error. Why should it be different than ?
I have no particular opinion on this. But the lint has code handling double negations: (it's treating double negations as if they weren't there at all)
The double negation is not the issue here. The value of evaluates to , as expected. But consider the following code: This will also compile just fine and print . It goes wrong in the cast . Rust doesn't check if cast overflows, so the value becomes .  can be changed to disallow casts that fail. (I'd be happy to implement this myself.) Or we can accept that these casts are can overflow silently.
I believe the issue is that --128 (aka 128) is an intermediate result of the computation that is out of bounds for the i8. EDIT: The issue is about the fact that the compiler doesn't complain.
jup, that was a misunderstanding of some code on my part + the issue that processing HIR works the wrong way around (you start with the outer most expression). Reverting should do the job
I thought that it was intended that would succesfully evaluate to . (That rustc knows that it's overflowing, but is smart to know that double negation will fix it in the end.) Pretend I said nothing.