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.
This commit is contained in:
parent
f9bbef7f44
commit
e919f82da1
6 changed files with 239 additions and 74 deletions
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
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<const_val, String> {
|
||||
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<const_val, String> {
|
||||
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<const_val, String> {
|
||||
#[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_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<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_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<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_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<const_val, ConstEvalErr> {
|
||||
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<const_val, ConstEvalErr> {
|
||||
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<const_val, ConstEvalErr> {
|
||||
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<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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue