From 09d3adf52eaf6e411b49d26af7d347406f563557 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 2 Oct 2015 10:36:45 +0200 Subject: [PATCH] implement RFC 1229 const eval errors outside of true constant enviroments are not reported anymore, but instead forwarded to a lint. --- src/librustc/lint/builtin.rs | 9 +- src/librustc/middle/check_const.rs | 8 +- src/librustc_trans/trans/_match.rs | 17 +- src/librustc_trans/trans/base.rs | 5 +- src/librustc_trans/trans/consts.rs | 259 +++++++++++++------ src/librustc_trans/trans/expr.rs | 120 +++++---- src/librustc_trans/trans/intrinsic.rs | 11 +- src/test/compile-fail/const-err.rs | 36 +++ src/test/compile-fail/const-eval-overflow.rs | 2 - src/test/compile-fail/issue-8460-const.rs | 42 +-- src/test/run-fail/overflowing-add.rs | 4 +- src/test/run-fail/overflowing-lsh-1.rs | 5 +- src/test/run-fail/overflowing-lsh-2.rs | 5 +- src/test/run-fail/overflowing-lsh-3.rs | 5 +- src/test/run-fail/overflowing-lsh-4.rs | 5 +- src/test/run-fail/overflowing-mul.rs | 5 +- src/test/run-fail/overflowing-neg.rs | 5 +- src/test/run-fail/overflowing-rsh-1.rs | 5 +- src/test/run-fail/overflowing-rsh-2.rs | 5 +- src/test/run-fail/overflowing-rsh-3.rs | 5 +- src/test/run-fail/overflowing-rsh-4.rs | 5 +- src/test/run-fail/overflowing-sub.rs | 5 +- src/test/run-pass/wrapping-int-api.rs | 2 - 23 files changed, 376 insertions(+), 194 deletions(-) create mode 100644 src/test/compile-fail/const-err.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index d9b05d80400..26f663b1c9d 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -16,6 +16,12 @@ use lint::{LintPass, LateLintPass, LintArray}; +declare_lint! { + pub CONST_ERR, + Warn, + "constant evaluation detected erroneous expression" +} + declare_lint! { pub UNUSED_IMPORTS, Warn, @@ -134,7 +140,8 @@ impl LintPass for HardwiredLints { VARIANT_SIZE_DIFFERENCES, FAT_PTR_TRANSMUTES, TRIVIAL_CASTS, - TRIVIAL_NUMERIC_CASTS + TRIVIAL_NUMERIC_CASTS, + CONST_ERR ) } } diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 662dfdfd4e7..433d2468a09 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -176,8 +176,8 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { if mode == Mode::ConstFn { for arg in &fd.inputs { match arg.pat.node { - hir::PatIdent(hir::BindByValue(hir::MutImmutable), _, None) => {} hir::PatWild(_) => {} + hir::PatIdent(hir::BindByValue(hir::MutImmutable), _, None) => {} _ => { span_err!(self.tcx.sess, arg.pat.span, E0022, "arguments of constant functions can only \ @@ -476,9 +476,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { self.tcx, ex, ExprTypeChecked) { Ok(_) => {} Err(msg) => { - span_err!(self.tcx.sess, msg.span, E0020, - "{} in a constant expression", - msg.description()) + self.tcx.sess.add_lint(::lint::builtin::CONST_ERR, ex.id, + msg.span, + msg.description().into_owned()) } } } diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index dbb0e120805..f0e4c20be05 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -275,19 +275,30 @@ impl<'a, 'tcx> Opt<'a, 'tcx> { } fn trans<'blk>(&self, mut bcx: Block<'blk, 'tcx>) -> OptResult<'blk, 'tcx> { + use trans::consts::TrueConst::Yes; let _icx = push_ctxt("match::trans_opt"); let ccx = bcx.ccx(); match *self { ConstantValue(ConstantExpr(lit_expr), _) => { let lit_ty = bcx.tcx().node_id_to_type(lit_expr.id); - let (llval, _) = consts::const_expr(ccx, &*lit_expr, bcx.fcx.param_substs, None); + let expr = consts::const_expr(ccx, &*lit_expr, bcx.fcx.param_substs, None, Yes); + let llval = match expr { + Ok((llval, _)) => llval, + Err(err) => bcx.ccx().sess().span_fatal(lit_expr.span, &err.description()), + }; let lit_datum = immediate_rvalue(llval, lit_ty); let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx)); SingleResult(Result::new(bcx, lit_datum.val)) } ConstantRange(ConstantExpr(ref l1), ConstantExpr(ref l2), _) => { - let (l1, _) = consts::const_expr(ccx, &**l1, bcx.fcx.param_substs, None); - let (l2, _) = consts::const_expr(ccx, &**l2, bcx.fcx.param_substs, None); + let l1 = match consts::const_expr(ccx, &**l1, bcx.fcx.param_substs, None, Yes) { + Ok((l1, _)) => l1, + Err(err) => bcx.ccx().sess().span_fatal(l1.span, &err.description()), + }; + let l2 = match consts::const_expr(ccx, &**l2, bcx.fcx.param_substs, None, Yes) { + Ok((l2, _)) => l2, + Err(err) => bcx.ccx().sess().span_fatal(l2.span, &err.description()), + }; RangeResult(Result::new(bcx, l1), Result::new(bcx, l2)) } Variant(disr_val, ref repr, _, _) => { diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 05b20ac3fb7..3c8c5b2d907 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -2125,7 +2125,10 @@ pub fn trans_item(ccx: &CrateContext, item: &hir::Item) { let mut v = TransItemVisitor{ ccx: ccx }; v.visit_expr(&**expr); - let g = consts::trans_static(ccx, m, expr, item.id, &item.attrs); + let g = match consts::trans_static(ccx, m, expr, item.id, &item.attrs) { + Ok(g) => g, + Err(err) => ccx.tcx().sess.span_fatal(expr.span, &err.description()), + }; set_global_section(ccx, g, item); update_linkage(ccx, g, Some(item.id), OriginalTranslation); }, diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index f509ed8dc5c..48f1e57a19c 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -15,7 +15,7 @@ use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr}; use llvm::{InternalLinkage, ValueRef, Bool, True}; use metadata::cstore::LOCAL_CRATE; use middle::{check_const, def}; -use middle::const_eval::{self, ConstVal}; +use middle::const_eval::{self, ConstVal, ConstEvalErr}; use middle::const_eval::{const_int_checked_neg, const_uint_checked_neg}; use middle::const_eval::{const_int_checked_add, const_uint_checked_add}; use middle::const_eval::{const_int_checked_sub, const_uint_checked_sub}; @@ -29,7 +29,10 @@ use middle::const_eval::eval_const_expr_partial; use middle::def_id::DefId; use trans::{adt, closure, debuginfo, expr, inline, machine}; use trans::base::{self, push_ctxt}; -use trans::common::*; +use trans::common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty}; +use trans::common::{type_is_sized, ExprOrMethodCall, node_id_substs, C_nil, const_get_elt}; +use trans::common::{C_struct, C_undef, const_to_opt_int, const_to_opt_uint, VariantInfo, C_uint}; +use trans::common::{type_is_fat_ptr, Field, C_vector, C_array, C_null, ExprId, MethodCallKey}; use trans::declare; use trans::monomorphize; use trans::type_::Type; @@ -44,6 +47,7 @@ use util::nodemap::NodeMap; use rustc_front::hir; use std::ffi::{CStr, CString}; +use std::borrow::Cow; use libc::c_uint; use syntax::ast; use syntax::attr; @@ -191,7 +195,8 @@ fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, node: ExprOrMethodCall, def_id: DefId, arg_vals: &[ValueRef], - param_substs: &'tcx Substs<'tcx>) -> ValueRef { + param_substs: &'tcx Substs<'tcx>, + trueconst: TrueConst) -> Result { let fn_like = const_eval::lookup_const_fn_by_id(ccx.tcx(), def_id); let fn_like = fn_like.expect("lookup_const_fn_by_id failed in const_fn_call"); @@ -204,9 +209,9 @@ fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let substs = ccx.tcx().mk_substs(node_id_substs(ccx, node, param_substs)); match fn_like.body().expr { Some(ref expr) => { - const_expr(ccx, &**expr, substs, Some(&fn_args)).0 - } - None => C_nil(ccx) + const_expr(ccx, &**expr, substs, Some(&fn_args), trueconst).map(|(res, _)| res) + }, + None => Ok(C_nil(ccx)), } } @@ -229,19 +234,57 @@ pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } +pub enum ConstEvalFailure { + /// in case the const evaluator failed on something that panic at runtime + /// as defined in RFC 1229 + Runtime(ConstEvalErr), + // in case we found a true constant + Compiletime(ConstEvalErr), +} + +impl ConstEvalFailure { + fn into_inner(self) -> ConstEvalErr { + match self { + Runtime(e) => e, + Compiletime(e) => e, + } + } + pub fn description(&self) -> Cow { + match self { + &Runtime(ref e) => e.description(), + &Compiletime(ref e) => e.description(), + } + } +} + +#[derive(Copy, Clone)] +pub enum TrueConst { + Yes, No +} + +use self::ConstEvalFailure::*; + fn get_const_val(ccx: &CrateContext, def_id: DefId, - ref_expr: &hir::Expr) -> ValueRef { + ref_expr: &hir::Expr) -> Result { let expr = get_const_expr(ccx, def_id, ref_expr); let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); - get_const_expr_as_global(ccx, expr, check_const::ConstQualif::empty(), empty_substs) + match get_const_expr_as_global(ccx, expr, check_const::ConstQualif::empty(), + empty_substs, TrueConst::Yes) { + Err(Runtime(err)) => { + ccx.tcx().sess.span_err(expr.span, &err.description()); + Err(Compiletime(err)) + }, + other => other, + } } pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, expr: &hir::Expr, qualif: check_const::ConstQualif, - param_substs: &'tcx Substs<'tcx>) - -> ValueRef { + param_substs: &'tcx Substs<'tcx>, + trueconst: TrueConst) + -> Result { debug!("get_const_expr_as_global: {:?}", expr.id); // Special-case constants to cache a common global for all uses. match expr.node { @@ -263,7 +306,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let key = (expr.id, param_substs); match ccx.const_values().borrow().get(&key) { - Some(&val) => return val, + Some(&val) => return Ok(val), None => {} } let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, @@ -271,9 +314,12 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let val = if qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS) { // Avoid autorefs as they would create global instead of stack // references, even when only the latter are correct. - const_expr_unadjusted(ccx, expr, ty, param_substs, None) + try!(const_expr_unadjusted(ccx, expr, ty, param_substs, None, trueconst)) } else { - const_expr(ccx, expr, param_substs, None).0 + match const_expr(ccx, expr, param_substs, None, trueconst) { + Err(err) => return Err(err), + Ok((ok, _)) => ok, + } }; // boolean SSA values are i1, but they have to be stored in i8 slots, @@ -288,17 +334,18 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let lvalue = addr_of(ccx, val, type_of::align_of(ccx, ty), "const"); ccx.const_values().borrow_mut().insert(key, lvalue); - lvalue + Ok(lvalue) } pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, e: &hir::Expr, param_substs: &'tcx Substs<'tcx>, - fn_args: FnArgMap) - -> (ValueRef, Ty<'tcx>) { + fn_args: FnArgMap, + trueconst: TrueConst) + -> Result<(ValueRef, Ty<'tcx>), ConstEvalFailure> { let ety = monomorphize::apply_param_substs(cx.tcx(), param_substs, &cx.tcx().expr_ty(e)); - let llconst = const_expr_unadjusted(cx, e, ety, param_substs, fn_args); + let llconst = try!(const_expr_unadjusted(cx, e, ety, param_substs, fn_args, trueconst)); let mut llconst = llconst; let mut ety_adjusted = monomorphize::apply_param_substs(cx.tcx(), param_substs, &cx.tcx().expr_ty_adjusted(e)); @@ -393,11 +440,11 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, e, ety_adjusted, csize, tsize)); } - (llconst, ety_adjusted) + Ok((llconst, ety_adjusted)) } fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty, - te: ValueRef) { + te: ValueRef, trueconst: TrueConst) -> Result<(), ConstEvalFailure> { // The only kind of unary expression that we check for validity // here is `-expr`, to check if it "overflows" (e.g. `-i32::MIN`). if let hir::ExprUnary(hir::UnNeg, ref inner_e) = e.node { @@ -410,13 +457,13 @@ fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty, // // Catch this up front by looking for ExprLit directly, // and just accepting it. - if let hir::ExprLit(_) = inner_e.node { return; } + if let hir::ExprLit(_) = inner_e.node { return Ok(()); } let result = match t.sty { ty::TyInt(int_type) => { let input = match const_to_opt_int(te) { Some(v) => v, - None => return, + None => return Ok(()), }; const_int_checked_neg( input, e, Some(const_eval::IntTy::from(cx.tcx(), int_type))) @@ -424,31 +471,51 @@ fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty, ty::TyUint(uint_type) => { let input = match const_to_opt_uint(te) { Some(v) => v, - None => return, + None => return Ok(()), }; const_uint_checked_neg( input, e, Some(const_eval::UintTy::from(cx.tcx(), uint_type))) } - _ => return, + _ => return Ok(()), }; + const_err(cx, e, result, trueconst) + } else { + Ok(()) + } +} - // We do not actually care about a successful result. - if let Err(err) = result { +fn const_err(cx: &CrateContext, + e: &hir::Expr, + result: Result, + trueconst: TrueConst) + -> Result<(), ConstEvalFailure> { + match (result, trueconst) { + (Ok(_), _) => { + // We do not actually care about a successful result. + Ok(()) + }, + (Err(err), TrueConst::Yes) => { cx.tcx().sess.span_err(e.span, &err.description()); - } + Err(Compiletime(err)) + }, + (Err(err), TrueConst::No) => { + cx.tcx().sess.span_warn(e.span, &err.description()); + Err(Runtime(err)) + }, } } fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty, - te1: ValueRef, te2: ValueRef) { - let b = if let hir::ExprBinary(b, _, _) = e.node { b } else { return }; + te1: ValueRef, te2: ValueRef, + trueconst: TrueConst) -> Result<(), ConstEvalFailure> { + let b = if let hir::ExprBinary(b, _, _) = e.node { b } else { unreachable!() }; let result = match t.sty { ty::TyInt(int_type) => { let (lhs, rhs) = match (const_to_opt_int(te1), const_to_opt_int(te2)) { (Some(v1), Some(v2)) => (v1, v2), - _ => return, + _ => return Ok(()), }; let opt_ety = Some(const_eval::IntTy::from(cx.tcx(), int_type)); @@ -460,14 +527,14 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty, hir::BiRem => const_int_checked_rem(lhs, rhs, e, opt_ety), hir::BiShl => const_int_checked_shl(lhs, rhs, e, opt_ety), hir::BiShr => const_int_checked_shr(lhs, rhs, e, opt_ety), - _ => return, + _ => return Ok(()), } } ty::TyUint(uint_type) => { let (lhs, rhs) = match (const_to_opt_uint(te1), const_to_opt_uint(te2)) { (Some(v1), Some(v2)) => (v1, v2), - _ => return, + _ => return Ok(()), }; let opt_ety = Some(const_eval::UintTy::from(cx.tcx(), uint_type)); @@ -479,43 +546,44 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty, hir::BiRem => const_uint_checked_rem(lhs, rhs, e, opt_ety), hir::BiShl => const_uint_checked_shl(lhs, rhs, e, opt_ety), hir::BiShr => const_uint_checked_shr(lhs, rhs, e, opt_ety), - _ => return, + _ => return Ok(()), } } - _ => return, + _ => return Ok(()), }; - // We do not actually care about a successful result. - if let Err(err) = result { - cx.tcx().sess.span_err(e.span, &err.description()); - } + const_err(cx, e, result, trueconst) } fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, e: &hir::Expr, ety: Ty<'tcx>, param_substs: &'tcx Substs<'tcx>, - fn_args: FnArgMap) - -> ValueRef + fn_args: FnArgMap, + trueconst: TrueConst) + -> Result { debug!("const_expr_unadjusted(e={:?}, ety={:?}, param_substs={:?})", e, ety, param_substs); - let map_list = |exprs: &[P]| -> Vec { + let map_list = |exprs: &[P]| -> Result, ConstEvalFailure> { exprs.iter() - .map(|e| const_expr(cx, &**e, param_substs, fn_args).0) + .map(|e| const_expr(cx, &**e, param_substs, fn_args, trueconst).map(|(l, _)| l)) + .collect::>>() + .into_iter() .collect() + // this dance is necessary to eagerly run const_expr so all errors are reported }; let _icx = push_ctxt("const_expr"); - match e.node { + Ok(match e.node { hir::ExprLit(ref lit) => { const_lit(cx, e, &**lit) }, hir::ExprBinary(b, ref e1, ref e2) => { /* Neither type is bottom, and we expect them to be unified * already, so the following is safe. */ - let (te1, ty) = const_expr(cx, &**e1, param_substs, fn_args); + let (te1, ty) = try!(const_expr(cx, &**e1, param_substs, fn_args, trueconst)); debug!("const_expr_unadjusted: te1={}, ty={:?}", cx.tn().val_to_string(te1), ty); @@ -523,9 +591,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let is_float = ty.is_fp(); let signed = ty.is_signed(); - let (te2, _) = const_expr(cx, &**e2, param_substs, fn_args); + let (te2, _) = try!(const_expr(cx, &**e2, param_substs, fn_args, trueconst)); - check_binary_expr_validity(cx, e, ty, te1, te2); + try!(check_binary_expr_validity(cx, e, ty, te1, te2, trueconst)); unsafe { match b.node { hir::BiAdd if is_float => llvm::LLVMConstFAdd(te1, te2), @@ -571,9 +639,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } } // unsafe { match b.node { }, hir::ExprUnary(u, ref inner_e) => { - let (te, ty) = const_expr(cx, &**inner_e, param_substs, fn_args); + let (te, ty) = try!(const_expr(cx, &**inner_e, param_substs, fn_args, trueconst)); - check_unary_expr_validity(cx, e, ty, te); + try!(check_unary_expr_validity(cx, e, ty, te, trueconst)); let is_float = ty.is_fp(); unsafe { match u { @@ -584,21 +652,21 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } } }, hir::ExprField(ref base, field) => { - let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); + let (bv, bt) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst)); let brepr = adt::represent_type(cx, bt); let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None); let ix = vinfo.field_index(field.node); adt::const_get_field(cx, &*brepr, bv, vinfo.discr, ix) }, hir::ExprTupField(ref base, idx) => { - let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); + let (bv, bt) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst)); let brepr = adt::represent_type(cx, bt); let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None); adt::const_get_field(cx, &*brepr, bv, vinfo.discr, idx.node) }, hir::ExprIndex(ref base, ref index) => { - let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); + let (bv, bt) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst)); let iv = match eval_const_expr_partial(cx.tcx(), &index, ExprTypeChecked) { Ok(ConstVal::Int(i)) => i as u64, Ok(ConstVal::Uint(u)) => u, @@ -650,10 +718,10 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, hir::ExprCast(ref base, _) => { let t_cast = ety; let llty = type_of::type_of(cx, t_cast); - let (v, t_expr) = const_expr(cx, &**base, param_substs, fn_args); + let (v, t_expr) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst)); debug!("trans_const_cast({:?} as {:?})", t_expr, t_cast); if expr::cast_is_noop(cx.tcx(), base, t_expr, t_cast) { - return v; + return Ok(v); } if type_is_fat_ptr(cx.tcx(), t_expr) { // Fat pointer casts. @@ -664,9 +732,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ptr_ty); if type_is_fat_ptr(cx.tcx(), t_cast) { let info = const_get_elt(cx, v, &[abi::FAT_PTR_EXTRA as u32]); - return C_struct(cx, &[addr, info], false) + return Ok(C_struct(cx, &[addr, info], false)) } else { - return addr; + return Ok(addr); } } unsafe { match ( @@ -732,35 +800,47 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } else { // If this isn't the address of a static, then keep going through // normal constant evaluation. - let (v, ty) = const_expr(cx, &**sub, param_substs, fn_args); + let (v, ty) = try!(const_expr(cx, &**sub, param_substs, fn_args, trueconst)); addr_of(cx, v, type_of::align_of(cx, ty), "ref") } }, hir::ExprAddrOf(hir::MutMutable, ref sub) => { - let (v, ty) = const_expr(cx, &**sub, param_substs, fn_args); + let (v, ty) = try!(const_expr(cx, &**sub, param_substs, fn_args, trueconst)); addr_of_mut(cx, v, type_of::align_of(cx, ty), "ref_mut_slice") }, hir::ExprTup(ref es) => { let repr = adt::represent_type(cx, ety); - let vals = map_list(&es[..]); + let vals = try!(map_list(&es[..])); adt::trans_const(cx, &*repr, 0, &vals[..]) }, hir::ExprStruct(_, ref fs, ref base_opt) => { let repr = adt::represent_type(cx, ety); let base_val = match *base_opt { - Some(ref base) => Some(const_expr(cx, &**base, param_substs, fn_args)), + Some(ref base) => Some(try!(const_expr( + cx, + &**base, + param_substs, + fn_args, + trueconst, + ))), None => None }; let VariantInfo { discr, fields } = VariantInfo::of_node(cx.tcx(), ety, e.id); let cs = fields.iter().enumerate().map(|(ix, &Field(f_name, _))| { match (fs.iter().find(|f| f_name == f.name.node), base_val) { - (Some(ref f), _) => const_expr(cx, &*f.expr, param_substs, fn_args).0, - (_, Some((bv, _))) => adt::const_get_field(cx, &*repr, bv, discr, ix), + (Some(ref f), _) => { + const_expr(cx, &*f.expr, param_substs, fn_args, trueconst).map(|(l, _)| l) + }, + (_, Some((bv, _))) => Ok(adt::const_get_field(cx, &*repr, bv, discr, ix)), (_, None) => cx.sess().span_bug(e.span, "missing struct field"), } - }).collect::>(); + }) + .collect::>>() + .into_iter() + .collect::,ConstEvalFailure>>(); + let cs = try!(cs); if ety.is_simd() { C_vector(&cs[..]) } else { @@ -771,8 +851,17 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let unit_ty = ety.sequence_element_type(cx.tcx()); let llunitty = type_of::type_of(cx, unit_ty); let vs = es.iter() - .map(|e| const_expr(cx, &**e, param_substs, fn_args).0) - .collect::>(); + .map(|e| const_expr( + cx, + &**e, + param_substs, + fn_args, + trueconst, + ).map(|(l, _)| l)) + .collect::>>() + .into_iter() + .collect::, ConstEvalFailure>>(); + let vs = try!(vs); // If the vector contains enums, an LLVM array won't work. if vs.iter().any(|vi| val_ty(*vi) != llunitty) { C_struct(cx, &vs[..], false) @@ -784,7 +873,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let unit_ty = ety.sequence_element_type(cx.tcx()); let llunitty = type_of::type_of(cx, unit_ty); let n = cx.tcx().eval_repeat_count(count); - let unit_val = const_expr(cx, &**elem, param_substs, fn_args).0; + let unit_val = try!(const_expr(cx, &**elem, param_substs, fn_args, trueconst)).0; let vs = vec![unit_val; n]; if val_ty(unit_val) != llunitty { C_struct(cx, &vs[..], false) @@ -806,7 +895,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val } def::DefConst(def_id) | def::DefAssociatedConst(def_id) => { - const_deref_ptr(cx, get_const_val(cx, def_id, e)) + const_deref_ptr(cx, try!(get_const_val(cx, def_id, e))) } def::DefVariant(enum_did, variant_did, _) => { let vinfo = cx.tcx().lookup_adt_def(enum_did).variant_with_id(variant_did); @@ -850,10 +939,17 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, }; } let def = cx.tcx().def_map.borrow()[&callee.id].full_def(); - let arg_vals = map_list(args); + let arg_vals = try!(map_list(args)); match def { def::DefFn(did, _) | def::DefMethod(did) => { - const_fn_call(cx, ExprId(callee.id), did, &arg_vals, param_substs) + try!(const_fn_call( + cx, + ExprId(callee.id), + did, + &arg_vals, + param_substs, + trueconst, + )) } def::DefStruct(_) => { if ety.is_simd() { @@ -875,15 +971,21 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } }, hir::ExprMethodCall(_, _, ref args) => { - let arg_vals = map_list(args); + let arg_vals = try!(map_list(args)); let method_call = ty::MethodCall::expr(e.id); let method_did = cx.tcx().tables.borrow().method_map[&method_call].def_id; - const_fn_call(cx, MethodCallKey(method_call), - method_did, &arg_vals, param_substs) + try!(const_fn_call(cx, MethodCallKey(method_call), + method_did, &arg_vals, param_substs, trueconst)) }, hir::ExprBlock(ref block) => { match block.expr { - Some(ref expr) => const_expr(cx, &**expr, param_substs, fn_args).0, + Some(ref expr) => try!(const_expr( + cx, + &**expr, + param_substs, + fn_args, + trueconst, + )).0, None => C_nil(cx), } }, @@ -902,20 +1004,27 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, }, _ => cx.sess().span_bug(e.span, "bad constant expression type in consts::const_expr"), - } + }) } + pub fn trans_static(ccx: &CrateContext, m: hir::Mutability, expr: &hir::Expr, id: ast::NodeId, attrs: &Vec) - -> ValueRef { + -> Result { unsafe { let _icx = push_ctxt("trans_static"); let g = base::get_item_val(ccx, id); let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); - let (v, _) = const_expr(ccx, expr, empty_substs, None); + let (v, _) = try!(const_expr( + ccx, + expr, + empty_substs, + None, + TrueConst::Yes, + ).map_err(|e| e.into_inner())); // boolean SSA values are i1, but they have to be stored in i8 slots, // otherwise some LLVM optimization passes don't work as expected @@ -964,7 +1073,7 @@ pub fn trans_static(ccx: &CrateContext, "thread_local") { llvm::set_thread_local(g, true); } - g + Ok(g) } } diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 6b9cec5ad24..6144de7109f 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -133,13 +133,25 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ) { if !qualif.intersects(check_const::ConstQualif::PREFER_IN_PLACE) { if let SaveIn(lldest) = dest { - let global = consts::get_const_expr_as_global(bcx.ccx(), expr, qualif, - bcx.fcx.param_substs); - // Cast pointer to destination, because constants - // have different types. - let lldest = PointerCast(bcx, lldest, val_ty(global)); - memcpy_ty(bcx, lldest, global, expr_ty_adjusted(bcx, expr)); - return bcx; + match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif, + bcx.fcx.param_substs, + consts::TrueConst::No) { + Ok(global) => { + // Cast pointer to destination, because constants + // have different types. + let lldest = PointerCast(bcx, lldest, val_ty(global)); + memcpy_ty(bcx, lldest, global, expr_ty_adjusted(bcx, expr)); + return bcx; + }, + Err(consts::ConstEvalFailure::Runtime(_)) => { + // in case const evaluation errors, translate normally + // debug assertions catch the same errors + // see RFC 1229 + }, + Err(consts::ConstEvalFailure::Compiletime(_)) => { + return bcx; + }, + } } // Even if we don't have a value to emit, and the expression // doesn't have any side-effects, we still have to translate the @@ -221,48 +233,64 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, check_const::ConstQualif::NOT_CONST | check_const::ConstQualif::NEEDS_DROP ) { - let global = consts::get_const_expr_as_global(bcx.ccx(), expr, qualif, - bcx.fcx.param_substs); + match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif, + bcx.fcx.param_substs, + consts::TrueConst::No) { + Ok(global) => { + if qualif.intersects(check_const::ConstQualif::HAS_STATIC_BORROWS) { + // Is borrowed as 'static, must return lvalue. - if qualif.intersects(check_const::ConstQualif::HAS_STATIC_BORROWS) { - // Is borrowed as 'static, must return lvalue. + // Cast pointer to global, because constants have different types. + let const_ty = expr_ty_adjusted(bcx, expr); + let llty = type_of::type_of(bcx.ccx(), const_ty); + let global = PointerCast(bcx, global, llty.ptr_to()); + let datum = Datum::new(global, const_ty, Lvalue::new("expr::trans")); + return DatumBlock::new(bcx, datum.to_expr_datum()); + } - // Cast pointer to global, because constants have different types. - let const_ty = expr_ty_adjusted(bcx, expr); - let llty = type_of::type_of(bcx.ccx(), const_ty); - let global = PointerCast(bcx, global, llty.ptr_to()); - let datum = Datum::new(global, const_ty, Lvalue::new("expr::trans")); - return DatumBlock::new(bcx, datum.to_expr_datum()); + // Otherwise, keep around and perform adjustments, if needed. + let const_ty = if adjusted_global { + expr_ty_adjusted(bcx, expr) + } else { + expr_ty(bcx, expr) + }; + + // This could use a better heuristic. + Some(if type_is_immediate(bcx.ccx(), const_ty) { + // Cast pointer to global, because constants have different types. + let llty = type_of::type_of(bcx.ccx(), const_ty); + let global = PointerCast(bcx, global, llty.ptr_to()); + // Maybe just get the value directly, instead of loading it? + immediate_rvalue(load_ty(bcx, global, const_ty), const_ty) + } else { + let scratch = alloc_ty(bcx, const_ty, "const"); + call_lifetime_start(bcx, scratch); + let lldest = if !const_ty.is_structural() { + // Cast pointer to slot, because constants have different types. + PointerCast(bcx, scratch, val_ty(global)) + } else { + // In this case, memcpy_ty calls llvm.memcpy after casting both + // source and destination to i8*, so we don't need any casts. + scratch + }; + memcpy_ty(bcx, lldest, global, const_ty); + Datum::new(scratch, const_ty, Rvalue::new(ByRef)) + }) + }, + Err(consts::ConstEvalFailure::Runtime(_)) => { + // in case const evaluation errors, translate normally + // debug assertions catch the same errors + // see RFC 1229 + None + }, + Err(consts::ConstEvalFailure::Compiletime(_)) => { + // generate a dummy llvm value + let const_ty = expr_ty(bcx, expr); + let llty = type_of::type_of(bcx.ccx(), const_ty); + let dummy = C_undef(llty.ptr_to()); + Some(Datum::new(dummy, const_ty, Rvalue::new(ByRef))) + }, } - - // Otherwise, keep around and perform adjustments, if needed. - let const_ty = if adjusted_global { - expr_ty_adjusted(bcx, expr) - } else { - expr_ty(bcx, expr) - }; - - // This could use a better heuristic. - Some(if type_is_immediate(bcx.ccx(), const_ty) { - // Cast pointer to global, because constants have different types. - let llty = type_of::type_of(bcx.ccx(), const_ty); - let global = PointerCast(bcx, global, llty.ptr_to()); - // Maybe just get the value directly, instead of loading it? - immediate_rvalue(load_ty(bcx, global, const_ty), const_ty) - } else { - let scratch = alloc_ty(bcx, const_ty, "const"); - call_lifetime_start(bcx, scratch); - let lldest = if !const_ty.is_structural() { - // Cast pointer to slot, because constants have different types. - PointerCast(bcx, scratch, val_ty(global)) - } else { - // In this case, memcpy_ty calls llvm.memcpy after casting both - // source and destination to i8*, so we don't need any casts. - scratch - }; - memcpy_ty(bcx, lldest, global, const_ty); - Datum::new(scratch, const_ty, Rvalue::new(ByRef)) - }) } else { None }; diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 0573d6301c5..b80cb7d5ca1 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -1563,7 +1563,16 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a> None => bcx.sess().span_bug(call_info.span, "intrinsic call with unexpected argument shape"), }; - let vector = consts::const_expr(bcx.ccx(), vector, tcx.mk_substs(substs), None).0; + let vector = match consts::const_expr( + bcx.ccx(), + vector, + tcx.mk_substs(substs), + None, + consts::TrueConst::Yes, // this should probably help simd error reporting + ) { + Ok((vector, _)) => vector, + Err(err) => bcx.sess().span_fatal(call_info.span, &err.description()), + }; let indices: Option> = (0..n) .map(|i| { diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs new file mode 100644 index 00000000000..be67e06d99f --- /dev/null +++ b/src/test/compile-fail/const-err.rs @@ -0,0 +1,36 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[allow(exceeding_bitshifts)] +#[deny(const_err)] + +fn black_box(_: T) { + unimplemented!() +} + +const BLA: u8 = 200u8 + 200u8; +//~^ ERROR attempted to add with overflow + +fn main() { + let a = -std::i8::MIN; + //~^ WARN attempted to negate with overflow + let b = 200u8 + 200u8 + 200u8; + //~^ WARN attempted to add with overflow + //~^^ WARN attempted to add with overflow + let c = 200u8 * 4; + //~^ WARN attempted to mul with overflow + let d = 42u8 - (42u8 + 1); + //~^ WARN attempted to sub with overflow + let _e = BLA; + black_box(a); + black_box(b); + black_box(c); + black_box(d); +} diff --git a/src/test/compile-fail/const-eval-overflow.rs b/src/test/compile-fail/const-eval-overflow.rs index f991e5328c1..daa60955ad8 100644 --- a/src/test/compile-fail/const-eval-overflow.rs +++ b/src/test/compile-fail/const-eval-overflow.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(negate_unsigned)] - #![allow(unused_imports)] #![feature(negate_unsigned)] diff --git a/src/test/compile-fail/issue-8460-const.rs b/src/test/compile-fail/issue-8460-const.rs index 9c2e8d278ab..95921556c7d 100644 --- a/src/test/compile-fail/issue-8460-const.rs +++ b/src/test/compile-fail/issue-8460-const.rs @@ -8,48 +8,50 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![deny(const_err)] + use std::{isize, i8, i16, i32, i64}; use std::thread; fn main() { assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); - //~^ ERROR attempted to divide with overflow in a constant expression + //~^ ERROR attempted to divide with overflow assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); - //~^ ERROR attempted to divide with overflow in a constant expression + //~^ ERROR attempted to divide with overflow assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); - //~^ ERROR attempted to divide with overflow in a constant expression + //~^ ERROR attempted to divide with overflow assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); - //~^ ERROR attempted to divide with overflow in a constant expression + //~^ ERROR attempted to divide with overflow assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); - //~^ ERROR attempted to divide with overflow in a constant expression + //~^ ERROR attempted to divide with overflow assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); - //~^ ERROR attempted to divide by zero in a constant expression + //~^ ERROR attempted to divide by zero assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); - //~^ ERROR attempted to divide by zero in a constant expression + //~^ ERROR attempted to divide by zero assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); - //~^ ERROR attempted to divide by zero in a constant expression + //~^ ERROR attempted to divide by zero assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); - //~^ ERROR attempted to divide by zero in a constant expression + //~^ ERROR attempted to divide by zero assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); - //~^ ERROR attempted to divide by zero in a constant expression + //~^ ERROR attempted to divide by zero assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow in a constant expression + //~^ ERROR attempted remainder with overflow assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow in a constant expression + //~^ ERROR attempted remainder with overflow assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow in a constant expression + //~^ ERROR attempted remainder with overflow assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow in a constant expression + //~^ ERROR attempted remainder with overflow assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow in a constant expression + //~^ ERROR attempted remainder with overflow assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero in a constant expression + //~^ ERROR attempted remainder with a divisor of zero assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero in a constant expression + //~^ ERROR attempted remainder with a divisor of zero assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero in a constant expression + //~^ ERROR attempted remainder with a divisor of zero assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero in a constant expression + //~^ ERROR attempted remainder with a divisor of zero assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero in a constant expression + //~^ ERROR attempted remainder with a divisor of zero } diff --git a/src/test/run-fail/overflowing-add.rs b/src/test/run-fail/overflowing-add.rs index cd13b817c2b..6c6a41fa6f2 100644 --- a/src/test/run-fail/overflowing-add.rs +++ b/src/test/run-fail/overflowing-add.rs @@ -11,9 +11,7 @@ // error-pattern:thread '
' panicked at 'arithmetic operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn value() -> u8 { 200 } fn main() { - let _x = value() + value() + value(); + let _x = 200u8 + 200u8 + 200u8; } diff --git a/src/test/run-fail/overflowing-lsh-1.rs b/src/test/run-fail/overflowing-lsh-1.rs index 54159153382..62935bacce8 100644 --- a/src/test/run-fail/overflowing-lsh-1.rs +++ b/src/test/run-fail/overflowing-lsh-1.rs @@ -11,9 +11,8 @@ // error-pattern:thread '
' panicked at 'shift operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn id(x: T) -> T { x } +#![warn(exceeding_bitshifts)] fn main() { - let _x = 1_i32 << id(32); + let _x = 1_i32 << 32; } diff --git a/src/test/run-fail/overflowing-lsh-2.rs b/src/test/run-fail/overflowing-lsh-2.rs index fd3e801457c..f6e6cb105c5 100644 --- a/src/test/run-fail/overflowing-lsh-2.rs +++ b/src/test/run-fail/overflowing-lsh-2.rs @@ -11,9 +11,8 @@ // error-pattern:thread '
' panicked at 'shift operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn id(x: T) -> T { x } +#![warn(exceeding_bitshifts)] fn main() { - let _x = 1 << id(-1); + let _x = 1 << -1; } diff --git a/src/test/run-fail/overflowing-lsh-3.rs b/src/test/run-fail/overflowing-lsh-3.rs index 58914bab3fb..a70f31954c6 100644 --- a/src/test/run-fail/overflowing-lsh-3.rs +++ b/src/test/run-fail/overflowing-lsh-3.rs @@ -11,9 +11,8 @@ // error-pattern:thread '
' panicked at 'shift operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn id(x: T) -> T { x } +#![warn(exceeding_bitshifts)] fn main() { - let _x = 1_u64 << id(64); + let _x = 1_u64 << 64; } diff --git a/src/test/run-fail/overflowing-lsh-4.rs b/src/test/run-fail/overflowing-lsh-4.rs index ed25876cec4..571feaeb943 100644 --- a/src/test/run-fail/overflowing-lsh-4.rs +++ b/src/test/run-fail/overflowing-lsh-4.rs @@ -14,12 +14,11 @@ // This function is checking that our automatic truncation does not // sidestep the overflow checking. -// (Work around constant-evaluation) -fn id(x: T) -> T { x } +#![warn(exceeding_bitshifts)] fn main() { // this signals overflow when checking is on - let x = 1_i8 << id(17); + let x = 1_i8 << 17; // ... but when checking is off, the fallback will truncate the // input to its lower three bits (= 1). Note that this is *not* diff --git a/src/test/run-fail/overflowing-mul.rs b/src/test/run-fail/overflowing-mul.rs index 5d2f5396240..a413a6f0abf 100644 --- a/src/test/run-fail/overflowing-mul.rs +++ b/src/test/run-fail/overflowing-mul.rs @@ -11,9 +11,6 @@ // error-pattern:thread '
' panicked at 'arithmetic operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn value() -> u8 { 200 } - fn main() { - let x = value() * 4; + let x = 200u8 * 4; } diff --git a/src/test/run-fail/overflowing-neg.rs b/src/test/run-fail/overflowing-neg.rs index cdb74c7d7e2..7891d1ce9be 100644 --- a/src/test/run-fail/overflowing-neg.rs +++ b/src/test/run-fail/overflowing-neg.rs @@ -11,9 +11,6 @@ // error-pattern:thread '
' panicked at 'attempted to negate with overflow' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn value() -> i8 { std::i8::MIN } - fn main() { - let _x = -value(); + let _x = -std::i8::MIN; } diff --git a/src/test/run-fail/overflowing-rsh-1.rs b/src/test/run-fail/overflowing-rsh-1.rs index c36a16f18f8..b58eaf7f836 100644 --- a/src/test/run-fail/overflowing-rsh-1.rs +++ b/src/test/run-fail/overflowing-rsh-1.rs @@ -11,9 +11,8 @@ // error-pattern:thread '
' panicked at 'shift operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn id(x: T) -> T { x } +#![warn(exceeding_bitshifts)] fn main() { - let _x = -1_i32 >> id(32); + let _x = -1_i32 >> 32; } diff --git a/src/test/run-fail/overflowing-rsh-2.rs b/src/test/run-fail/overflowing-rsh-2.rs index f619ebe9fb4..40b468a6ad4 100644 --- a/src/test/run-fail/overflowing-rsh-2.rs +++ b/src/test/run-fail/overflowing-rsh-2.rs @@ -11,9 +11,8 @@ // error-pattern:thread '
' panicked at 'shift operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn id(x: T) -> T { x } +#![warn(exceeding_bitshifts)] fn main() { - let _x = -1_i32 >> id(-1); + let _x = -1_i32 >> -1; } diff --git a/src/test/run-fail/overflowing-rsh-3.rs b/src/test/run-fail/overflowing-rsh-3.rs index c261e195fd7..afe6a908cb5 100644 --- a/src/test/run-fail/overflowing-rsh-3.rs +++ b/src/test/run-fail/overflowing-rsh-3.rs @@ -11,9 +11,8 @@ // error-pattern:thread '
' panicked at 'shift operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn id(x: T) -> T { x } +#![warn(exceeding_bitshifts)] fn main() { - let _x = -1_i64 >> id(64); + let _x = -1_i64 >> 64; } diff --git a/src/test/run-fail/overflowing-rsh-4.rs b/src/test/run-fail/overflowing-rsh-4.rs index 6e79a13d4e1..585186575f6 100644 --- a/src/test/run-fail/overflowing-rsh-4.rs +++ b/src/test/run-fail/overflowing-rsh-4.rs @@ -14,12 +14,11 @@ // This function is checking that our (type-based) automatic // truncation does not sidestep the overflow checking. -// (Work around constant-evaluation) -fn id(x: T) -> T { x } +#![warn(exceeding_bitshifts)] fn main() { // this signals overflow when checking is on - let x = 2_i8 >> id(17); + let x = 2_i8 >> 17; // ... but when checking is off, the fallback will truncate the // input to its lower three bits (= 1). Note that this is *not* diff --git a/src/test/run-fail/overflowing-sub.rs b/src/test/run-fail/overflowing-sub.rs index b089dccbaa5..ece4d37c36e 100644 --- a/src/test/run-fail/overflowing-sub.rs +++ b/src/test/run-fail/overflowing-sub.rs @@ -11,9 +11,6 @@ // error-pattern:thread '
' panicked at 'arithmetic operation overflowed' // compile-flags: -C debug-assertions -// (Work around constant-evaluation) -fn value() -> u8 { 42 } - fn main() { - let _x = value() - (value() + 1); + let _x = 42u8 - (42u8 + 1); } diff --git a/src/test/run-pass/wrapping-int-api.rs b/src/test/run-pass/wrapping-int-api.rs index 48eea120623..5470ad93e14 100644 --- a/src/test/run-pass/wrapping-int-api.rs +++ b/src/test/run-pass/wrapping-int-api.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(num_wrapping)] - // Test inherent wrapping_* methods for {i,u}{size,8,16,32,64}. use std::{i8, i16, i32, i64, isize};