about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2015-02-22 16:34:26 +0100
committerFelix S. Klock II <pnkfelix@pnkfx.org>2015-03-03 12:10:57 +0100
commite919f82da1fb9fa0409f8febede311de4a8f1703 (patch)
tree6f6afaca95486b6b6cfad1675952331630efb662
parentf9bbef7f448ba843052eb88733c79aa36c35d5ab (diff)
downloadrust-e919f82da1fb9fa0409f8febede311de4a8f1703.tar.gz
rust-e919f82da1fb9fa0409f8febede311de4a8f1703.zip
Address arith-overflow and error-handling in `const_eval.rs`.
 1. Detect and report arithmetic overflow during const-expr eval.

 2. Instead `eval_const_expr_partial` returning `Err(String)`, it now
    has a dedicated enum of different cases. The main benefit of this
    is the ability to pass along an interpretable payload, namely the
    two inputs that caused an overlfow.

I attempted to minimize fallout to error output in tests, but some was
unavoidable. Those changes are in a follow-on commit.
-rw-r--r--src/librustc/middle/check_const.rs5
-rw-r--r--src/librustc/middle/const_eval.rs266
-rw-r--r--src/librustc/middle/ty.rs5
-rw-r--r--src/librustc_typeck/astconv.rs18
-rw-r--r--src/librustc_typeck/check/mod.rs5
-rw-r--r--src/test/compile-fail/eval-enum.rs4
6 files changed, 234 insertions, 69 deletions
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 8401d25024d..497022ac6ac 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -307,8 +307,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
                             match const_eval::eval_const_expr_partial(self.tcx, ex, None) {
                                 Ok(_) => {}
                                 Err(msg) => {
-                                    span_err!(self.tcx.sess, ex.span, E0020,
-                                              "{} in a constant expression", msg)
+                                    span_err!(self.tcx.sess, msg.span, E0020,
+                                              "{} in a constant expression",
+                                              msg.description())
                                 }
                             }
                         }
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index 23844739a6b..51bf5a6db5b 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -25,6 +25,7 @@ use syntax::parse::token::InternedString;
 use syntax::ptr::P;
 use syntax::{ast_map, ast_util, codemap};
 
+use std::borrow::{Cow, IntoCow};
 use std::num::wrapping::OverflowingOps;
 use std::cmp::Ordering;
 use std::collections::hash_map::Entry::Vacant;
@@ -203,42 +204,193 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat>
 pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> const_val {
     match eval_const_expr_partial(tcx, e, None) {
         Ok(r) => r,
-        Err(s) => tcx.sess.span_fatal(e.span, &s[..])
+        Err(s) => tcx.sess.span_fatal(s.span, s.description().as_slice())
     }
 }
 
-fn checked_add_int(a: i64, b: i64) -> Result<const_val, String> {
+
+#[derive(Clone)]
+pub struct ConstEvalErr {
+    pub span: Span,
+    pub kind: ErrKind,
+}
+
+#[derive(Clone)]
+pub enum ErrKind {
+    CannotCast,
+    CannotCastTo(&'static str),
+    InvalidOpForBools(ast::BinOp_),
+    InvalidOpForFloats(ast::BinOp_),
+    InvalidOpForIntUint(ast::BinOp_),
+    InvalidOpForUintInt(ast::BinOp_),
+    NegateOnString,
+    NegateOnBoolean,
+    NotOnFloat,
+    NotOnString,
+
+    AddiWithOverflow(i64, i64),
+    SubiWithOverflow(i64, i64),
+    MuliWithOverflow(i64, i64),
+    AdduWithOverflow(u64, u64),
+    SubuWithOverflow(u64, u64),
+    MuluWithOverflow(u64, u64),
+    DivideByZero,
+    DivideWithOverflow,
+    ModuloByZero,
+    ModuloWithOverflow,
+    MissingStructField,
+    NonConstPath,
+    NonConstStruct,
+    TupleIndexOutOfBounds,
+
+    MiscBinaryOp,
+    MiscCatchAll,
+}
+
+impl ConstEvalErr {
+    pub fn description(&self) -> Cow<str> {
+        use self::ErrKind::*;
+        match self.kind {
+            CannotCast => "can't cast this type".into_cow(),
+            CannotCastTo(s) => format!("can't cast this type to {}", s).into_cow(),
+            InvalidOpForBools(_) =>  "can't do this op on bools".into_cow(),
+            InvalidOpForFloats(_) => "can't do this op on floats".into_cow(),
+            InvalidOpForIntUint(..) => "can't do this op on an int and uint".into_cow(),
+            InvalidOpForUintInt(..) => "can't do this op on a uint and int".into_cow(),
+            NegateOnString => "negate on string".into_cow(),
+            NegateOnBoolean => "negate on boolean".into_cow(),
+            NotOnFloat => "not on float or string".into_cow(),
+            NotOnString => "not on float or string".into_cow(),
+
+            AddiWithOverflow(..) => "attempted to add with overflow".into_cow(),
+            SubiWithOverflow(..) => "attempted to sub with overflow".into_cow(),
+            MuliWithOverflow(..) => "attempted to mul with overflow".into_cow(),
+            AdduWithOverflow(..) => "attempted to add with overflow".into_cow(),
+            SubuWithOverflow(..) => "attempted to sub with overflow".into_cow(),
+            MuluWithOverflow(..) => "attempted to mul with overflow".into_cow(),
+            DivideByZero         => "attempted to divide by zero".into_cow(),
+            DivideWithOverflow   => "attempted to divide with overflow".into_cow(),
+            ModuloByZero         => "attempted remainder with a divisor of zero".into_cow(),
+            ModuloWithOverflow   => "attempted remainder with overflow".into_cow(),
+            MissingStructField  => "nonexistent struct field".into_cow(),
+            NonConstPath        => "non-constant path in constant expr".into_cow(),
+            NonConstStruct      => "non-constant struct in constant expr".into_cow(),
+            TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(),
+
+            MiscBinaryOp => "bad operands for binary".into_cow(),
+            MiscCatchAll => "unsupported constant expr".into_cow(),
+        }
+    }
+}
+
+fn invalid_op_for_bools(e: &Expr, op: ast::BinOp_) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::InvalidOpForBools(op) }
+}
+fn invalid_op_for_floats(e: &Expr, op: ast::BinOp_) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::InvalidOpForFloats(op) }
+}
+fn invalid_op_for_int_uint(e: &Expr, op: ast::BinOp_) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::InvalidOpForIntUint(op) }
+}
+fn invalid_op_for_uint_int(e: &Expr, op: ast::BinOp_) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::InvalidOpForUintInt(op) }
+}
+fn negate_on_string(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::NegateOnString }
+}
+fn negate_on_boolean(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::NegateOnBoolean }
+}
+fn not_on_float(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::NotOnFloat }
+}
+fn not_on_string(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::NotOnString }
+}
+
+fn addi_with_overflow(e: &Expr, a: i64, b: i64) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::AddiWithOverflow(a, b) }
+}
+fn subi_with_overflow(e: &Expr, a: i64, b: i64) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::SubiWithOverflow(a, b) }
+}
+fn muli_with_overflow(e: &Expr, a: i64, b: i64) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::MuliWithOverflow(a, b) }
+}
+fn addu_with_overflow(e: &Expr, a: u64, b: u64) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::AdduWithOverflow(a, b) }
+}
+fn subu_with_overflow(e: &Expr, a: u64, b: u64) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::SubuWithOverflow(a, b) }
+}
+fn mulu_with_overflow(e: &Expr, a: u64, b: u64) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::MuluWithOverflow(a, b) }
+}
+fn divide_by_zero(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::DivideByZero }
+}
+fn divide_with_overflow(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::DivideWithOverflow }
+}
+fn modulo_by_zero(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::ModuloByZero }
+}
+fn modulo_with_overflow(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::ModuloWithOverflow }
+}
+fn missing_struct_field(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::MissingStructField }
+}
+fn non_const_path(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::NonConstPath }
+}
+fn non_const_struct(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::NonConstStruct }
+}
+fn tuple_index_out_of_bounds(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::TupleIndexOutOfBounds }
+}
+
+fn misc_binary_op(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::MiscBinaryOp }
+}
+fn misc_catch_all(e: &Expr) -> ConstEvalErr {
+    ConstEvalErr { span: e.span, kind: ErrKind::MiscCatchAll }
+}
+
+
+fn checked_add_int(e: &Expr, a: i64, b: i64) -> Result<const_val, ConstEvalErr> {
     let (ret, oflo) = a.overflowing_add(b);
-    if !oflo { Ok(const_int(ret)) } else { Err(format!("constant arithmetic overflow")) }
+    if !oflo { Ok(const_int(ret)) } else { Err(addi_with_overflow(e, a, b)) }
 }
-fn checked_sub_int(a: i64, b: i64) -> Result<const_val, String> {
+fn checked_sub_int(e: &Expr, a: i64, b: i64) -> Result<const_val, ConstEvalErr> {
     let (ret, oflo) = a.overflowing_sub(b);
-    if !oflo { Ok(const_int(ret)) } else { Err(format!("constant arithmetic overflow")) }
+    if !oflo { Ok(const_int(ret)) } else { Err(subi_with_overflow(e, a, b)) }
 }
-fn checked_mul_int(a: i64, b: i64) -> Result<const_val, String> {
+fn checked_mul_int(e: &Expr, a: i64, b: i64) -> Result<const_val, ConstEvalErr> {
     let (ret, oflo) = a.overflowing_mul(b);
-    if !oflo { Ok(const_int(ret)) } else { Err(format!("constant arithmetic overflow")) }
+    if !oflo { Ok(const_int(ret)) } else { Err(muli_with_overflow(e, a, b)) }
 }
 
-fn checked_add_uint(a: u64, b: u64) -> Result<const_val, String> {
+fn checked_add_uint(e: &Expr, a: u64, b: u64) -> Result<const_val, ConstEvalErr> {
     let (ret, oflo) = a.overflowing_add(b);
-    if !oflo { Ok(const_uint(ret)) } else { Err(format!("constant arithmetic overflow")) }
+    if !oflo { Ok(const_uint(ret)) } else { Err(addu_with_overflow(e, a, b)) }
 }
-fn checked_sub_uint(a: u64, b: u64) -> Result<const_val, String> {
+fn checked_sub_uint(e: &Expr, a: u64, b: u64) -> Result<const_val, ConstEvalErr> {
     let (ret, oflo) = a.overflowing_sub(b);
-    if !oflo { Ok(const_uint(ret)) } else { Err(format!("constant arithmetic overflow")) }
+    if !oflo { Ok(const_uint(ret)) } else { Err(subu_with_overflow(e, a, b)) }
 }
-fn checked_mul_uint(a: u64, b: u64) -> Result<const_val, String> {
+fn checked_mul_uint(e: &Expr, a: u64, b: u64) -> Result<const_val, ConstEvalErr> {
     let (ret, oflo) = a.overflowing_mul(b);
-    if !oflo { Ok(const_uint(ret)) } else { Err(format!("constant arithmetic overflow")) }
+    if !oflo { Ok(const_uint(ret)) } else { Err(mulu_with_overflow(e, a, b)) }
 }
 
 
 pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
                                      e: &Expr,
                                      ty_hint: Option<Ty<'tcx>>)
-                                     -> Result<const_val, String> {
-    fn fromb(b: bool) -> Result<const_val, String> { Ok(const_int(b as i64)) }
+                                     -> Result<const_val, ConstEvalErr> {
+    fn fromb<T>(b: bool) -> Result<const_val, T> { Ok(const_int(b as i64)) }
 
     let ety = ty_hint.or_else(|| ty::expr_ty_opt(tcx, e));
 
@@ -248,9 +400,9 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
           Ok(const_float(f)) => Ok(const_float(-f)),
           Ok(const_int(i)) => Ok(const_int(-i)),
           Ok(const_uint(i)) => Ok(const_uint(-i)),
-          Ok(const_str(_)) => Err("negate on string".to_string()),
-          Ok(const_bool(_)) => Err("negate on boolean".to_string()),
-          ref err => ((*err).clone())
+          Ok(const_str(_)) => Err(negate_on_string(e)),
+          Ok(const_bool(_)) => Err(negate_on_boolean(e)),
+          err => err
         }
       }
       ast::ExprUnary(ast::UnNot, ref inner) => {
@@ -258,7 +410,9 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
           Ok(const_int(i)) => Ok(const_int(!i)),
           Ok(const_uint(i)) => Ok(const_uint(!i)),
           Ok(const_bool(b)) => Ok(const_bool(!b)),
-          _ => Err("not on float or string".to_string())
+          Ok(const_str(_)) => Err(not_on_string(e)),
+          Ok(const_float(_)) => Err(not_on_float(e)),
+          err => err
         }
       }
       ast::ExprBinary(op, ref a, ref b) => {
@@ -281,7 +435,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
               ast::BiNe => fromb(a != b),
               ast::BiGe => fromb(a >= b),
               ast::BiGt => fromb(a > b),
-              _ => Err("can't do this op on floats".to_string())
+              _ => Err(invalid_op_for_floats(e, op.node)),
             }
           }
           (Ok(const_int(a)), Ok(const_int(b))) => {
@@ -304,23 +458,23 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
                 }
             };
             match op.node {
-              ast::BiAdd => checked_add_int(a, b),
-              ast::BiSub => checked_sub_int(a, b),
-              ast::BiMul => checked_mul_int(a, b),
+              ast::BiAdd => checked_add_int(e, a, b),
+              ast::BiSub => checked_sub_int(e, a, b),
+              ast::BiMul => checked_mul_int(e, a, b),
               ast::BiDiv => {
                   if b == 0 {
-                      Err("attempted to divide by zero".to_string())
+                      Err(divide_by_zero(e))
                   } else if b == -1 && is_a_min_value() {
-                      Err("attempted to divide with overflow".to_string())
+                      Err(divide_with_overflow(e))
                   } else {
                       Ok(const_int(a / b))
                   }
               }
               ast::BiRem => {
                   if b == 0 {
-                      Err("attempted remainder with a divisor of zero".to_string())
+                      Err(modulo_by_zero(e))
                   } else if b == -1 && is_a_min_value() {
-                      Err("attempted remainder with overflow".to_string())
+                      Err(modulo_with_overflow(e))
                   } else {
                       Ok(const_int(a % b))
                   }
@@ -340,17 +494,12 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
           }
           (Ok(const_uint(a)), Ok(const_uint(b))) => {
             match op.node {
-              ast::BiAdd => checked_add_uint(a, b),
-              ast::BiSub => checked_sub_uint(a, b),
-              ast::BiMul => checked_mul_uint(a, b),
-              ast::BiDiv if b == 0 => {
-                  Err("attempted to divide by zero".to_string())
-              }
+              ast::BiAdd => checked_add_uint(e, a, b),
+              ast::BiSub => checked_sub_uint(e, a, b),
+              ast::BiMul => checked_mul_uint(e, a, b),
+              ast::BiDiv if b == 0 => Err(divide_by_zero(e)),
               ast::BiDiv => Ok(const_uint(a / b)),
-              ast::BiRem if b == 0 => {
-                  Err("attempted remainder with a divisor of \
-                       zero".to_string())
-              }
+              ast::BiRem if b == 0 => Err(modulo_by_zero(e)),
               ast::BiRem => Ok(const_uint(a % b)),
               ast::BiAnd | ast::BiBitAnd => Ok(const_uint(a & b)),
               ast::BiOr | ast::BiBitOr => Ok(const_uint(a | b)),
@@ -370,14 +519,14 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
             match op.node {
               ast::BiShl => Ok(const_int(a << b as uint)),
               ast::BiShr => Ok(const_int(a >> b as uint)),
-              _ => Err("can't do this op on an int and uint".to_string())
+              _ => Err(invalid_op_for_int_uint(e, op.node)),
             }
           }
           (Ok(const_uint(a)), Ok(const_int(b))) => {
             match op.node {
               ast::BiShl => Ok(const_uint(a << b as uint)),
               ast::BiShr => Ok(const_uint(a >> b as uint)),
-              _ => Err("can't do this op on a uint and int".to_string())
+              _ => Err(invalid_op_for_uint_int(e, op.node)),
             }
           }
           (Ok(const_bool(a)), Ok(const_bool(b))) => {
@@ -389,10 +538,13 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
               ast::BiBitOr => a | b,
               ast::BiEq => a == b,
               ast::BiNe => a != b,
-              _ => return Err("can't do this op on bools".to_string())
+              _ => return Err(invalid_op_for_bools(e, op.node)),
              }))
           }
-          _ => Err("bad operands for binary".to_string())
+          (err @ Err(..), _) |
+          (_, err @ Err(..)) => err,
+
+          _ => Err(misc_binary_op(e)),
         }
       }
       ast::ExprCast(ref base, ref target_ty) => {
@@ -407,7 +559,10 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
         // Prefer known type to noop, but always have a type hint.
         let base_hint = ty::expr_ty_opt(tcx, &**base).unwrap_or(ety);
         let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint)));
-        cast_const(val, ety)
+        match cast_const(val, ety) {
+            Ok(val) => Ok(val),
+            Err(kind) => Err(ConstEvalErr { span: e.span, kind: kind }),
+        }
       }
       ast::ExprPath(..) => {
           let opt_def = tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
@@ -434,7 +589,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
           };
           let const_expr = match const_expr {
               Some(actual_e) => actual_e,
-              None => return Err("non-constant path in constant expr".to_string())
+              None => return Err(non_const_path(e)),
           };
           let ety = ety.or_else(|| const_ty.and_then(|ty| ast_ty_to_prim_ty(tcx, ty)));
           eval_const_expr_partial(tcx, const_expr, ety)
@@ -456,11 +611,11 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
             if fields.len() > index.node {
                 return eval_const_expr_partial(tcx, &*fields[index.node], None)
             } else {
-                return Err("tuple index out of bounds".to_string())
+                return Err(tuple_index_out_of_bounds(e))
             }
         }
 
-        Err("non-constant struct in constant expr".to_string())
+        Err(non_const_struct(e))
       }
       ast::ExprField(ref base, field_name) => {
         // Get the base expression if it is a struct and it is constant
@@ -471,17 +626,17 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
                                            f.ident.node.as_str() == field_name.node.as_str()) {
                 return eval_const_expr_partial(tcx, &*f.expr, None)
             } else {
-                return Err("nonexistent struct field".to_string())
+                return Err(missing_struct_field(e));
             }
         }
 
-        Err("non-constant struct in constant expr".to_string())
+        Err(non_const_struct(e))
       }
-      _ => Err("unsupported constant expr".to_string())
+      _ => Err(misc_catch_all(e))
     }
 }
 
-fn cast_const(val: const_val, ty: Ty) -> Result<const_val, String> {
+fn cast_const(val: const_val, ty: Ty) -> Result<const_val, ErrKind> {
     macro_rules! define_casts {
         ($($ty_pat:pat => (
             $intermediate_ty:ty,
@@ -494,11 +649,10 @@ fn cast_const(val: const_val, ty: Ty) -> Result<const_val, String> {
                     const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
                     const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
                     const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
-                    _ => Err(concat!("can't cast this type to ",
-                                     stringify!($const_type)).to_string())
+                    _ => Err(ErrKind::CannotCastTo(stringify!($const_type))),
                 }
             },)*
-            _ => Err("can't cast this type".to_string())
+            _ => Err(ErrKind::CannotCast),
         })
     }
 
@@ -572,15 +726,15 @@ pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
                                -> Option<Ordering> {
     let a = match eval_const_expr_partial(tcx, a, ty_hint) {
         Ok(a) => a,
-        Err(s) => {
-            tcx.sess.span_err(a.span, &s[..]);
+        Err(e) => {
+            tcx.sess.span_err(a.span, e.description().as_slice());
             return None;
         }
     };
     let b = match eval_const_expr_partial(tcx, b, ty_hint) {
         Ok(b) => b,
-        Err(s) => {
-            tcx.sess.span_err(b.span, &s[..]);
+        Err(e) => {
+            tcx.sess.span_err(b.span, e.description().as_slice());
             return None;
         }
     };
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index b6d1fc5a369..62947d796b7 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -5366,8 +5366,9 @@ pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
                                                       "expected signed integer constant");
                                         }
                                         Err(err) => {
-                                            span_err!(cx.sess, e.span, E0305,
-                                                      "expected constant: {}", err);
+                                            span_err!(cx.sess, err.span, E0305,
+                                                      "constant evaluation error: {}",
+                                                      err.description().as_slice());
                                         }
                                     }
                                 } else {
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 87604f4a4f8..832de5251b5 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1390,14 +1390,22 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
                             ty::mk_vec(tcx, ast_ty_to_ty(this, rscope, &**ty),
                                         Some(i as uint)),
                         _ => {
-                            span_fatal!(tcx.sess, ast_ty.span, E0249,
-                                        "expected constant expr for array length");
+                            span_err!(tcx.sess, ast_ty.span, E0249,
+                                      "expected constant expr for array length");
+                            this.tcx().types.err
                         }
                     }
                 }
-                Err(r) => {
-                    span_fatal!(tcx.sess, ast_ty.span, E0250,
-                                "expected constant expr for array length: {}", r);
+                Err(ref r) => {
+                    let subspan  =
+                        ast_ty.span.lo <= r.span.lo && r.span.hi <= ast_ty.span.hi;
+                    span_err!(tcx.sess, ast_ty.span, E0250,
+                              "array length constant evaluation error: {}",
+                              r.description().as_slice());
+                    if !subspan {
+                        span_note!(tcx.sess, ast_ty.span, "for array length here")
+                    }
+                    this.tcx().types.err
                 }
             }
         }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 10771935e22..a12ff04912c 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -4604,8 +4604,9 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
                                 "expected signed integer constant");
                         }
                         Err(ref err) => {
-                            span_err!(ccx.tcx.sess, e.span, E0080,
-                                "expected constant: {}", *err);
+                            span_err!(ccx.tcx.sess, err.span, E0080,
+                                      "constant evaluation error: {}",
+                                      err.description().as_slice());
                         }
                     }
                 },
diff --git a/src/test/compile-fail/eval-enum.rs b/src/test/compile-fail/eval-enum.rs
index 92b7b601e4d..ed1327f3118 100644
--- a/src/test/compile-fail/eval-enum.rs
+++ b/src/test/compile-fail/eval-enum.rs
@@ -9,8 +9,8 @@
 // except according to those terms.
 
 enum test {
-    div_zero = 1/0, //~ERROR expected constant: attempted to divide by zero
-    rem_zero = 1%0  //~ERROR expected constant: attempted remainder with a divisor of zero
+    div_zero = 1/0, //~ERROR constant evaluation error: attempted to divide by zero
+    rem_zero = 1%0  //~ERROR constant evaluation error: attempted remainder with a divisor of zero
 }
 
 fn main() {}