diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 8401d25024d3..497022ac6ac4 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 23844739a6be..51bf5a6db5bb 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 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 { - let (ret, oflo) = a.overflowing_add(b); - if !oflo { Ok(const_int(ret)) } else { Err(format!("constant arithmetic overflow")) } -} -fn checked_sub_int(a: i64, b: i64) -> Result { - let (ret, oflo) = a.overflowing_sub(b); - if !oflo { Ok(const_int(ret)) } else { Err(format!("constant arithmetic overflow")) } -} -fn checked_mul_int(a: i64, b: i64) -> Result { - let (ret, oflo) = a.overflowing_mul(b); - if !oflo { Ok(const_int(ret)) } else { Err(format!("constant arithmetic overflow")) } + +#[derive(Clone)] +pub struct ConstEvalErr { + pub span: Span, + pub kind: ErrKind, } -fn checked_add_uint(a: u64, b: u64) -> Result { +#[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 { + 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 { let (ret, oflo) = a.overflowing_add(b); - if !oflo { Ok(const_uint(ret)) } else { Err(format!("constant arithmetic overflow")) } + if !oflo { Ok(const_int(ret)) } else { Err(addi_with_overflow(e, a, b)) } } -fn checked_sub_uint(a: u64, b: u64) -> Result { +fn checked_sub_int(e: &Expr, a: i64, b: i64) -> Result { let (ret, oflo) = a.overflowing_sub(b); - if !oflo { Ok(const_uint(ret)) } else { Err(format!("constant arithmetic overflow")) } + if !oflo { Ok(const_int(ret)) } else { Err(subi_with_overflow(e, a, b)) } } -fn checked_mul_uint(a: u64, b: u64) -> Result { +fn checked_mul_int(e: &Expr, a: i64, b: i64) -> Result { let (ret, oflo) = a.overflowing_mul(b); - if !oflo { Ok(const_uint(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(e: &Expr, a: u64, b: u64) -> Result { + let (ret, oflo) = a.overflowing_add(b); + if !oflo { Ok(const_uint(ret)) } else { Err(addu_with_overflow(e, a, b)) } +} +fn checked_sub_uint(e: &Expr, a: u64, b: u64) -> Result { + let (ret, oflo) = a.overflowing_sub(b); + if !oflo { Ok(const_uint(ret)) } else { Err(subu_with_overflow(e, a, b)) } +} +fn checked_mul_uint(e: &Expr, a: u64, b: u64) -> Result { + let (ret, oflo) = a.overflowing_mul(b); + 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>) - -> Result { - fn fromb(b: bool) -> Result { Ok(const_int(b as i64)) } + -> Result { + fn fromb(b: bool) -> Result { 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 { +fn cast_const(val: const_val, ty: Ty) -> Result { macro_rules! define_casts { ($($ty_pat:pat => ( $intermediate_ty:ty, @@ -494,11 +649,10 @@ fn cast_const(val: const_val, ty: Ty) -> Result { 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 { 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 b6d1fc5a3694..62947d796b7e 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 87604f4a4f80..832de5251b50 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 10771935e22a..a12ff04912c4 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 92b7b601e4dc..ed1327f31185 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() {}