diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index bb7a628f3ba..67cf5473f79 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -80,7 +80,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Unary { op, arg } => { let arg = unpack!(block = this.as_operand(block, arg)); // Check for -MIN on signed integers - if op == UnOp::Neg && expr.ty.is_signed() && this.check_overflow() { + if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() { let bool_ty = this.hir.bool_ty(); let minval = this.minval_literal(expr_span, expr.ty); @@ -247,7 +247,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { lhs: Operand<'tcx>, rhs: Operand<'tcx>) -> BlockAnd> { let scope_id = self.innermost_scope_id(); let bool_ty = self.hir.bool_ty(); - if op.is_checkable() && ty.is_integral() && self.check_overflow() { + if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() { let result_tup = self.hir.tcx().mk_tup(vec![ty, bool_ty]); let result_value = self.temp(result_tup); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index f3b1a871388..9d7818a9ba4 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -55,8 +55,6 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { cached_resume_block: Option, /// cached block with the RETURN terminator cached_return_block: Option, - - has_warned_about_xcrate_overflows: bool } struct CFG<'tcx> { @@ -275,8 +273,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { var_indices: FnvHashMap(), unit_temp: None, cached_resume_block: None, - cached_return_block: None, - has_warned_about_xcrate_overflows: false + cached_return_block: None }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); @@ -381,21 +378,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } } - - pub fn check_overflow(&mut self) -> bool { - let check = self.hir.tcx().sess.opts.debugging_opts.force_overflow_checks - .unwrap_or(self.hir.tcx().sess.opts.debug_assertions); - - if !check && self.hir.may_be_inlined_cross_crate() { - if !self.has_warned_about_xcrate_overflows { - self.hir.tcx().sess.span_warn(self.fn_span, - "overflow checks would be missing when used from another crate"); - self.has_warned_about_xcrate_overflows = true; - } - } - - check - } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 659a326e852..25860ae7ef1 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -30,7 +30,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use syntax::parse::token; use rustc::hir; use rustc_const_math::{ConstInt, ConstUsize}; -use syntax::attr; +use syntax::attr::AttrMetaMethods; #[derive(Copy, Clone)] pub struct Cx<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { @@ -38,42 +38,49 @@ pub struct Cx<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, constness: hir::Constness, - /// True if this MIR can get inlined in other crates. - inline: bool + /// True if this constant/function needs overflow checks. + check_overflow: bool } impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, src: MirSource) -> Cx<'a, 'gcx, 'tcx> { - let (constness, inline) = match src { + let constness = match src { MirSource::Const(_) | - MirSource::Static(..) => (hir::Constness::Const, false), + MirSource::Static(..) => hir::Constness::Const, MirSource::Fn(id) => { - let def_id = infcx.tcx.map.local_def_id(id); let fn_like = FnLikeNode::from_node(infcx.tcx.map.get(id)); match fn_like.map(|f| f.kind()) { - Some(FnKind::ItemFn(_, _, _, c, _, _, attrs)) => { - let scheme = infcx.tcx.lookup_item_type(def_id); - let any_types = !scheme.generics.types.is_empty(); - (c, any_types || attr::requests_inline(attrs)) - } - Some(FnKind::Method(_, m, _, attrs)) => { - let scheme = infcx.tcx.lookup_item_type(def_id); - let any_types = !scheme.generics.types.is_empty(); - (m.constness, any_types || attr::requests_inline(attrs)) - } - _ => (hir::Constness::NotConst, true) + Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => c, + Some(FnKind::Method(_, m, _, _)) => m.constness, + _ => hir::Constness::NotConst } } MirSource::Promoted(..) => bug!() }; + let attrs = infcx.tcx.map.attrs(src.item_id()); + + // Some functions always have overflow checks enabled, + // however, they may not get codegen'd, depending on + // the settings for the crate they are translated in. + let mut check_overflow = attrs.iter().any(|item| { + item.check_name("rustc_inherit_overflow_checks") + }); + + // Respect -Z force-overflow-checks=on and -C debug-assertions. + check_overflow |= infcx.tcx.sess.opts.debugging_opts.force_overflow_checks + .unwrap_or(infcx.tcx.sess.opts.debug_assertions); + + // Constants and const fn's always need overflow checks. + check_overflow |= constness == hir::Constness::Const; + Cx { tcx: infcx.tcx, infcx: infcx, constness: constness, - inline: inline + check_overflow: check_overflow } } } @@ -186,8 +193,8 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { self.tcx } - pub fn may_be_inlined_cross_crate(&self) -> bool { - self.inline + pub fn check_overflow(&self) -> bool { + self.check_overflow } } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index d019677d0e6..3e3908845e3 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -16,7 +16,7 @@ use rustc::mir::repr as mir; use asm; use base; use callee::Callee; -use common::{self, val_ty, C_null, C_uint, BlockAndBuilder, Result}; +use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder, Result}; use datum::{Datum, Lvalue}; use debuginfo::DebugLoc; use adt; @@ -579,6 +579,15 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { lhs: ValueRef, rhs: ValueRef, input_ty: Ty<'tcx>) -> OperandValue { + // This case can currently arise only from functions marked + // with #[rustc_inherit_overflow_checks] and inlined from + // another crate (mostly core::num generic/#[inline] fns), + // while the current crate doesn't use overflow checks. + if !bcx.ccx().check_overflow() { + let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty); + return OperandValue::Pair(val, C_bool(bcx.ccx(), false)); + } + let (val, of) = match op { // These are checked using intrinsics mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c11347f5762..86c4a33896d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -502,6 +502,13 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat is just used to make tests pass \ and will never be stable", cfg_fn!(rustc_attrs))), + ("rustc_inherit_overflow_checks", Whitelisted, Gated("rustc_attrs", + "the `#[rustc_inherit_overflow_checks]` \ + attribute is just used to control \ + overflow checking behavior of several \ + libcore functions that are inlined \ + across crates and will never be stable", + cfg_fn!(rustc_attrs))), ("allow_internal_unstable", Normal, Gated("allow_internal_unstable", EXPLAIN_ALLOW_INTERNAL_UNSTABLE,