diff --git a/src/libcollections/bit.rs b/src/libcollections/bit.rs index c1b34c52fcc15..0ae912eebdfa0 100644 --- a/src/libcollections/bit.rs +++ b/src/libcollections/bit.rs @@ -816,11 +816,11 @@ impl Bitv { let full_value = if value { !0 } else { 0 }; // Correct the old tail word, setting or clearing formerly unused bits - let old_last_word = blocks_for_bits(self.nbits) - 1; + let num_cur_blocks = blocks_for_bits(self.nbits); if self.nbits % u32::BITS > 0 { let mask = mask_for_bits(self.nbits); if value { - self.storage[old_last_word] |= !mask; + self.storage[num_cur_blocks - 1] |= !mask; } else { // Extra bits are already zero by invariant. } @@ -828,7 +828,7 @@ impl Bitv { // Fill in words after the old tail word let stop_idx = cmp::min(self.storage.len(), new_nblocks); - for idx in range(old_last_word + 1, stop_idx) { + for idx in range(num_cur_blocks, stop_idx) { self.storage[idx] = full_value; } diff --git a/src/libcore/hash/sip.rs b/src/libcore/hash/sip.rs index c20fb8457d26e..14e5719da97e8 100644 --- a/src/libcore/hash/sip.rs +++ b/src/libcore/hash/sip.rs @@ -71,17 +71,17 @@ macro_rules! u8to64_le { macro_rules! rotl { ($x:expr, $b:expr) => - (($x << $b) | ($x >> (64 - $b))) + (($x << $b) | ($x >> (64.wrapping_sub($b)))) } macro_rules! compress { ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => ({ - $v0 += $v1; $v1 = rotl!($v1, 13); $v1 ^= $v0; + $v0 = $v0.wrapping_add($v1); $v1 = rotl!($v1, 13); $v1 ^= $v0; $v0 = rotl!($v0, 32); - $v2 += $v3; $v3 = rotl!($v3, 16); $v3 ^= $v2; - $v0 += $v3; $v3 = rotl!($v3, 21); $v3 ^= $v0; - $v2 += $v1; $v1 = rotl!($v1, 17); $v1 ^= $v2; + $v2 = $v2.wrapping_add($v3); $v3 = rotl!($v3, 16); $v3 ^= $v2; + $v0 = $v0.wrapping_add($v3); $v3 = rotl!($v3, 21); $v3 ^= $v0; + $v2 = $v2.wrapping_add($v1); $v1 = rotl!($v1, 17); $v1 ^= $v2; $v2 = rotl!($v2, 32); }) } diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 5924d515dda59..ac5727b1b8955 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -547,6 +547,15 @@ extern "rust-intrinsic" { pub fn u64_mul_with_overflow(x: u64, y: u64) -> (u64, bool); } +#[cfg(not(stage0))] +extern "rust-intrinsic" { + /// Returns (a + b) mod 2^N, where N is the width of N in bits. + pub fn overflowing_add(a: T, b: T) -> T; + /// Returns (a - b) mod 2^N, where N is the width of N in bits. + pub fn overflowing_sub(a: T, b: T) -> T; + /// Returns (a * b) mod 2^N, where N is the width of N in bits. + pub fn overflowing_mul(a: T, b: T) -> T; +} /// `TypeId` represents a globally unique identifier for a type #[lang="type_id"] // This needs to be kept in lockstep with the code in trans/intrinsic.rs and diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 57415f4331d6d..fdd2a86e87b5e 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -29,6 +29,9 @@ use option::Option; use option::Option::{Some, None}; use str::{FromStr, StrExt}; +#[experimental = "may be removed or relocated"] +pub mod wrapping; + /// A built-in signed or unsigned integer. #[stable] pub trait Int diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs new file mode 100644 index 0000000000000..de971ad001c38 --- /dev/null +++ b/src/libcore/num/wrapping.rs @@ -0,0 +1,152 @@ +// Copyright 2014 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(missing_docs)] + +use ops::*; + +#[cfg(not(stage0))] +use intrinsics::{overflowing_add, overflowing_sub, overflowing_mul}; + +pub trait WrappingOps { + fn wrapping_add(self, rhs: Self) -> Self; + fn wrapping_sub(self, rhs: Self) -> Self; + fn wrapping_mul(self, rhs: Self) -> Self; +} + +#[cfg(not(stage0))] +macro_rules! wrapping_impl { + ($($t:ty)*) => ($( + impl WrappingOps for $t { + #[inline(always)] + fn wrapping_add(self, rhs: $t) -> $t { + unsafe { + overflowing_add(self, rhs) + } + } + #[inline(always)] + fn wrapping_sub(self, rhs: $t) -> $t { + unsafe { + overflowing_sub(self, rhs) + } + } + #[inline(always)] + fn wrapping_mul(self, rhs: $t) -> $t { + unsafe { + overflowing_mul(self, rhs) + } + } + } + )*) +} + +#[cfg(stage0)] +macro_rules! wrapping_impl { + ($($t:ty)*) => ($( + impl WrappingOps for $t { + #[inline(always)] + fn wrapping_add(self, rhs: $t) -> $t { + self + rhs + } + #[inline(always)] + fn wrapping_sub(self, rhs: $t) -> $t { + self - rhs + } + #[inline(always)] + fn wrapping_mul(self, rhs: $t) -> $t { + self * rhs + } + } + )*) +} + +wrapping_impl! { uint u8 u16 u32 u64 int i8 i16 i32 i64 } + +#[derive(PartialEq,Eq,PartialOrd,Ord,Clone,Copy)] +pub struct Wrapping(pub T); + +impl Add for Wrapping { + type Output = Wrapping; + + #[inline(always)] + fn add(self, other: Wrapping) -> Wrapping { + Wrapping(self.0.wrapping_add(other.0)) + } +} + +impl Sub for Wrapping { + type Output = Wrapping; + + #[inline(always)] + fn sub(self, other: Wrapping) -> Wrapping { + Wrapping(self.0.wrapping_sub(other.0)) + } +} + +impl Mul for Wrapping { + type Output = Wrapping; + + #[inline(always)] + fn mul(self, other: Wrapping) -> Wrapping { + Wrapping(self.0.wrapping_mul(other.0)) + } +} + +impl> Not for Wrapping { + type Output = Wrapping; + + fn not(self) -> Wrapping { + Wrapping(!self.0) + } +} + +impl> BitXor for Wrapping { + type Output = Wrapping; + + #[inline(always)] + fn bitxor(self, other: Wrapping) -> Wrapping { + Wrapping(self.0 ^ other.0) + } +} + +impl> BitOr for Wrapping { + type Output = Wrapping; + + #[inline(always)] + fn bitor(self, other: Wrapping) -> Wrapping { + Wrapping(self.0 | other.0) + } +} + +impl> BitAnd for Wrapping { + type Output = Wrapping; + + #[inline(always)] + fn bitand(self, other: Wrapping) -> Wrapping { + Wrapping(self.0 & other.0) + } +} + +impl> Shl for Wrapping { + type Output = Wrapping; + + #[inline(always)] + fn shl(self, other: uint) -> Wrapping { + Wrapping(self.0 << other) + } +} + +impl> Shr for Wrapping { + type Output = Wrapping; + + #[inline(always)] + fn shr(self, other: uint) -> Wrapping { + Wrapping(self.0 >> other) + } +} diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index ab956587d8248..0520cccfbeeb9 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -773,6 +773,7 @@ macro_rules! shr_impl { shr_impl! { uint u8 u16 u32 u64 int i8 i16 i32 i64 } + /// The `Index` trait is used to specify the functionality of indexing operations /// like `arr[idx]` when used in an immutable context. /// diff --git a/src/libcore/prelude.rs b/src/libcore/prelude.rs index c3bb9c91557f3..84501b0ea737a 100644 --- a/src/libcore/prelude.rs +++ b/src/libcore/prelude.rs @@ -32,6 +32,8 @@ pub use marker::{Copy, Send, Sized, Sync}; pub use ops::{Drop, Fn, FnMut, FnOnce, FullRange}; +pub use num::wrapping::{Wrapping, WrappingOps}; + // Reexported functions pub use iter::range; pub use mem::drop; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 6051c68b116ba..26edf8d7a1d3a 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -789,6 +789,7 @@ impl TwoWaySearcher { // critical factorization (u, v) and p = period(v) #[inline] fn maximal_suffix(arr: &[u8], reversed: bool) -> (uint, uint) { + use num::wrapping::WrappingOps; let mut left = -1; // Corresponds to i in the paper let mut right = 0; // Corresponds to j in the paper let mut offset = 1; // Corresponds to k in the paper @@ -798,17 +799,17 @@ impl TwoWaySearcher { let a; let b; if reversed { - a = arr[left + offset]; + a = arr[left.wrapping_add(offset)]; b = arr[right + offset]; } else { a = arr[right + offset]; - b = arr[left + offset]; + b = arr[left.wrapping_add(offset)]; } if a < b { // Suffix is smaller, period is entire prefix so far. right += offset; offset = 1; - period = right - left; + period = right.wrapping_sub(left); } else if a == b { // Advance through repetition of the current period. if offset == period { @@ -825,7 +826,7 @@ impl TwoWaySearcher { period = 1; } } - (left + 1, period) + (left.wrapping_add(1), period) } } diff --git a/src/librand/isaac.rs b/src/librand/isaac.rs index 84328360ce324..e9113a85f2f1e 100644 --- a/src/librand/isaac.rs +++ b/src/librand/isaac.rs @@ -304,22 +304,23 @@ impl Isaac64Rng { fn init(&mut self, use_rsl: bool) { macro_rules! init { ($var:ident) => ( - let mut $var = 0x9e3779b97f4a7c13; + let mut $var = Wrapping(0x9e3779b97f4a7c13); ) } + init!(a); init!(b); init!(c); init!(d); init!(e); init!(f); init!(g); init!(h); macro_rules! mix { () => {{ - a-=e; f^=h>>9; h+=a; - b-=f; g^=a<<9; a+=b; - c-=g; h^=b>>23; b+=c; - d-=h; a^=c<<15; c+=d; - e-=a; b^=d>>14; d+=e; - f-=b; c^=e<<20; e+=f; - g-=c; d^=f>>17; f+=g; - h-=d; e^=g<<14; g+=h; + a=a-e; f=f^h>>9; h=h+a; + b=b-f; g=g^a<<9; a=a+b; + c=c-g; h=h^b>>23; b=b+c; + d=d-h; a=a^c<<15; c=c+d; + e=e-a; b=b^d>>14; d=d+e; + f=f-b; c=c^e<<20; e=e+f; + g=g-c; d=d^f>>17; f=f+g; + h=h-d; e=e^g<<14; g=g+h; }} } @@ -331,15 +332,15 @@ impl Isaac64Rng { macro_rules! memloop { ($arr:expr) => {{ for i in range(0, RAND_SIZE_64 / 8).map(|i| i * 8) { - a+=$arr[i ]; b+=$arr[i+1]; - c+=$arr[i+2]; d+=$arr[i+3]; - e+=$arr[i+4]; f+=$arr[i+5]; - g+=$arr[i+6]; h+=$arr[i+7]; + a=a+Wrapping($arr[i ]); b=b+Wrapping($arr[i+1]); + c=c+Wrapping($arr[i+2]); d=d+Wrapping($arr[i+3]); + e=e+Wrapping($arr[i+4]); f=f+Wrapping($arr[i+5]); + g=g+Wrapping($arr[i+6]); h=h+Wrapping($arr[i+7]); mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ]=a.0; self.mem[i+1]=b.0; + self.mem[i+2]=c.0; self.mem[i+3]=d.0; + self.mem[i+4]=e.0; self.mem[i+5]=f.0; + self.mem[i+6]=g.0; self.mem[i+7]=h.0; } }} } @@ -349,10 +350,10 @@ impl Isaac64Rng { } else { for i in range(0, RAND_SIZE_64 / 8).map(|i| i * 8) { mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ]=a.0; self.mem[i+1]=b.0; + self.mem[i+2]=c.0; self.mem[i+3]=d.0; + self.mem[i+4]=e.0; self.mem[i+5]=f.0; + self.mem[i+6]=g.0; self.mem[i+7]=h.0; } } @@ -363,8 +364,8 @@ impl Isaac64Rng { fn isaac64(&mut self) { self.c += 1; // abbreviations - let mut a = self.a; - let mut b = self.b + self.c; + let mut a = Wrapping(self.a); + let mut b = Wrapping(self.b) + Wrapping(self.c); const MIDPOINT: uint = RAND_SIZE_64 / 2; const MP_VEC: [(uint, uint); 2] = [(0,MIDPOINT), (MIDPOINT, 0)]; macro_rules! ind { @@ -383,13 +384,13 @@ impl Isaac64Rng { let mix = if $j == 0 {!mix} else {mix}; unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; + let x = Wrapping(*self.mem.get_unchecked(base + mr_offset)); + a = mix + Wrapping(*self.mem.get_unchecked(base + m2_offset)); + let y = Wrapping(ind!(x.0)) + a + b; + *self.mem.get_unchecked_mut(base + mr_offset) = y.0; - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; + b = Wrapping(ind!(y.0 >> RAND_SIZE_64_LEN)) + x; + *self.rsl.get_unchecked_mut(base + mr_offset) = b.0; } }} } @@ -401,13 +402,13 @@ impl Isaac64Rng { let mix = if $j == 0 {!mix} else {mix}; unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; + let x = Wrapping(*self.mem.get_unchecked(base + mr_offset)); + a = mix + Wrapping(*self.mem.get_unchecked(base + m2_offset)); + let y = Wrapping(ind!(x.0)) + a + b; + *self.mem.get_unchecked_mut(base + mr_offset) = y.0; - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; + b = Wrapping(ind!(y.0 >> RAND_SIZE_64_LEN)) + x; + *self.rsl.get_unchecked_mut(base + mr_offset) = b.0; } }} } @@ -419,8 +420,8 @@ impl Isaac64Rng { } } - self.a = a; - self.b = b; + self.a = a.0; + self.b = b.0; self.cnt = RAND_SIZE_64; } } diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 5ac8f908bf164..02c40a15847e1 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -743,7 +743,7 @@ pub fn get_enum_variants<'tcx>(intr: Rc, cdata: Cmd, id: ast::Nod _ => { /* empty */ } } let old_disr_val = disr_val; - disr_val += 1; + disr_val = disr_val.wrapping_add(1); Rc::new(ty::VariantInfo { args: arg_tys, arg_names: arg_names, diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index c8921f1b2fb93..8a3bdbd1352d0 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -358,7 +358,7 @@ fn encode_enum_variant_info(ecx: &EncodeContext, ecx.tcx.map.with_path(variant.node.id, |path| encode_path(rbml_w, path)); rbml_w.end_tag(); - disr_val += 1; + disr_val = disr_val.wrapping_add(1); i += 1; } } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 550c0f34cafec..4c3103343a193 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -202,7 +202,9 @@ impl<'a, 'b, 'tcx> DecodeContext<'a, 'b, 'tcx> { pub fn tr_id(&self, id: ast::NodeId) -> ast::NodeId { // from_id_range should be non-empty assert!(!self.from_id_range.empty()); - (id - self.from_id_range.min + self.to_id_range.min) + // Use wrapping arithmetic because otherwise it introduces control flow. + // Maybe we should just have the control flow? -- aatch + (id.wrapping_sub(self.from_id_range.min).wrapping_add(self.to_id_range.min)) } /// Translates an EXTERNAL def-id, converting the crate number from the one used in the encoded diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index ef86e67de1606..446fed05a24ee 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5317,6 +5317,7 @@ pub fn type_is_empty(cx: &ctxt, ty: Ty) -> bool { pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId) -> Rc>>> { + use std::num::Int; // For checked_add memoized(&cx.enum_var_cache, id, |id: ast::DefId| { if ast::LOCAL_CRATE != id.krate { Rc::new(csearch::get_enum_variants(cx, id)) @@ -5333,10 +5334,7 @@ pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId) let mut last_discriminant: Option = None; Rc::new(enum_definition.variants.iter().map(|variant| { - let mut discriminant = match last_discriminant { - Some(val) => val + 1, - None => INITIAL_DISCRIMINANT_VALUE - }; + let mut discriminant = INITIAL_DISCRIMINANT_VALUE; match variant.node.disr_expr { Some(ref e) => @@ -5359,7 +5357,19 @@ pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId) *err)[]); } }, - None => {} + None => { + if let Some(val) = last_discriminant { + if let Some(v) = val.checked_add(1) { + discriminant = v + } else { + cx.sess.span_err( + variant.span, + &format!("Discriminant overflowed!")[]); + } + } else { + discriminant = INITIAL_DISCRIMINANT_VALUE; + } + } }; last_discriminant = Some(discriminant); diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index d9bb1d769bfbe..172268996d5ff 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -257,7 +257,6 @@ pub enum CrateType { CrateTypeStaticlib, } - #[derive(Clone)] pub enum Passes { SomePasses(Vec), @@ -348,7 +347,8 @@ macro_rules! options { #[allow(non_upper_case_globals, dead_code)] mod $mod_desc { pub const parse_bool: Option<&'static str> = None; - pub const parse_opt_bool: Option<&'static str> = None; + pub const parse_opt_bool: Option<&'static str> = + Some("one of: `y`, `yes`, `on`, `n`, `no`, or `off`"); pub const parse_string: Option<&'static str> = Some("a string"); pub const parse_opt_string: Option<&'static str> = Some("a string"); pub const parse_list: Option<&'static str> = Some("a space-separated list of strings"); @@ -379,7 +379,19 @@ macro_rules! options { fn parse_opt_bool(slot: &mut Option, v: Option<&str>) -> bool { match v { - Some(..) => false, + Some(s) => { + match s { + "n" | "no" | "off" => { + *slot = Some(false); + } + "y" | "yes" | "on" => { + *slot = Some(true); + } + _ => { return false; } + } + + true + }, None => { *slot = Some(true); true } } } @@ -582,6 +594,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "Adds unstable command line options to rustc interface"), print_enum_sizes: bool = (false, parse_bool, "Print the size of enums and their variants"), + force_overflow_checks: Option = (None, parse_opt_bool, + "Force overflow checks on or off"), } pub fn default_lib_output() -> CrateType { diff --git a/src/librustc/util/nodemap.rs b/src/librustc/util/nodemap.rs index 044534ae85236..e726cafa93e60 100644 --- a/src/librustc/util/nodemap.rs +++ b/src/librustc/util/nodemap.rs @@ -88,7 +88,7 @@ impl Writer for FnvHasher { let FnvHasher(mut hash) = *self; for byte in bytes.iter() { hash = hash ^ (*byte as u64); - hash = hash * 0x100000001b3; + hash = hash.wrapping_mul(0x100000001b3); } *self = FnvHasher(hash); } diff --git a/src/librustc_back/sha2.rs b/src/librustc_back/sha2.rs index ac5662f534c86..998c27c840444 100644 --- a/src/librustc_back/sha2.rs +++ b/src/librustc_back/sha2.rs @@ -347,17 +347,19 @@ impl Engine256State { // Sha-512 and Sha-256 use basically the same calculations which are implemented // by these macros. Inlining the calculations seems to result in better generated code. macro_rules! schedule_round { ($t:expr) => ( - w[$t] = sigma1(w[$t - 2]) + w[$t - 7] + sigma0(w[$t - 15]) + w[$t - 16]; - ) + w[$t] = sigma1(w[$t - 2]).wrapping_add(w[$t - 7]) + .wrapping_add(sigma0(w[$t - 15])).wrapping_add(w[$t - 16]); + ) } macro_rules! sha2_round { ($A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident, $G:ident, $H:ident, $K:ident, $t:expr) => ( { - $H += sum1($E) + ch($E, $F, $G) + $K[$t] + w[$t]; - $D += $H; - $H += sum0($A) + maj($A, $B, $C); + $H = $H.wrapping_add(sum1($E)).wrapping_add(ch($E, $F, $G)) + .wrapping_add($K[$t]).wrapping_add(w[$t]); + $D = $D.wrapping_add($H); + $H = $H.wrapping_add(sum0($A)).wrapping_add(maj($A, $B, $C)); } ) } @@ -397,14 +399,14 @@ impl Engine256State { sha2_round!(b, c, d, e, f, g, h, a, K32, t + 7); } - self.h0 += a; - self.h1 += b; - self.h2 += c; - self.h3 += d; - self.h4 += e; - self.h5 += f; - self.h6 += g; - self.h7 += h; + self.h0 = self.h0.wrapping_add(a); + self.h1 = self.h1.wrapping_add(b); + self.h2 = self.h2.wrapping_add(c); + self.h3 = self.h3.wrapping_add(d); + self.h4 = self.h4.wrapping_add(e); + self.h5 = self.h5.wrapping_add(f); + self.h6 = self.h6.wrapping_add(g); + self.h7 = self.h7.wrapping_add(h); } } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 88ce36a710a0c..76c2194e07b31 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -3111,6 +3111,12 @@ pub fn trans_crate<'tcx>(analysis: ty::CrateAnalysis<'tcx>) let ty::CrateAnalysis { ty_cx: tcx, export_map, reachable, name, .. } = analysis; let krate = tcx.map.krate(); + let check_overflow = if let Some(v) = tcx.sess.opts.debugging_opts.force_overflow_checks { + v + } else { + !attr::contains_name(&krate.config[], "ndebug") + }; + // Before we touch LLVM, make sure that multithreading is enabled. unsafe { use std::sync::{Once, ONCE_INIT}; @@ -3138,7 +3144,8 @@ pub fn trans_crate<'tcx>(analysis: ty::CrateAnalysis<'tcx>) export_map, Sha256::new(), link_meta.clone(), - reachable); + reachable, + check_overflow); { let ccx = shared_ccx.get_ccx(0); diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 1abf3b0b886cc..5d9ef7254d894 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -68,6 +68,7 @@ pub struct SharedCrateContext<'tcx> { symbol_hasher: RefCell, tcx: ty::ctxt<'tcx>, stats: Stats, + check_overflow: bool, available_monomorphizations: RefCell>, available_drop_glues: RefCell, String>>, @@ -241,7 +242,8 @@ impl<'tcx> SharedCrateContext<'tcx> { export_map: ExportMap, symbol_hasher: Sha256, link_meta: LinkMeta, - reachable: NodeSet) + reachable: NodeSet, + check_overflow: bool) -> SharedCrateContext<'tcx> { let (metadata_llcx, metadata_llmod) = unsafe { create_context_and_module(&tcx.sess, "metadata") @@ -270,6 +272,7 @@ impl<'tcx> SharedCrateContext<'tcx> { llvm_insns: RefCell::new(FnvHashMap::new()), fn_stats: RefCell::new(Vec::new()), }, + check_overflow: check_overflow, available_monomorphizations: RefCell::new(FnvHashSet::new()), available_drop_glues: RefCell::new(FnvHashMap::new()), }; @@ -733,6 +736,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &format!("the type `{}` is too big for the current architecture", obj.repr(self.tcx()))[]) } + + pub fn check_overflow(&self) -> bool { + self.shared.check_overflow + } } fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index ac50445be2f9b..dd0d4814ca0bb 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -65,6 +65,7 @@ use trans::machine::{llsize_of, llsize_of_alloc}; use trans::type_::Type; use syntax::{ast, ast_util, codemap}; +use syntax::parse::token::InternedString; use syntax::print::pprust::{expr_to_string}; use syntax::ptr::P; use syntax::parse::token; @@ -1689,22 +1690,34 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }; let is_float = ty::type_is_fp(intype); let is_signed = ty::type_is_signed(intype); - let rhs = base::cast_shift_expr_rhs(bcx, op, lhs, rhs); + let sp = binop_expr.span; let mut bcx = bcx; let val = match op { ast::BiAdd => { if is_float { FAdd(bcx, lhs, rhs) } - else { Add(bcx, lhs, rhs) } + else { + let (newbcx, res) = with_overflow_check(bcx, OverflowOp::Add, sp, lhs_t, lhs, rhs); + bcx = newbcx; + res + } } ast::BiSub => { if is_float { FSub(bcx, lhs, rhs) } - else { Sub(bcx, lhs, rhs) } + else { + let (newbcx, res) = with_overflow_check(bcx, OverflowOp::Sub, sp, lhs_t, lhs, rhs); + bcx = newbcx; + res + } } ast::BiMul => { if is_float { FMul(bcx, lhs, rhs) } - else { Mul(bcx, lhs, rhs) } + else { + let (newbcx, res) = with_overflow_check(bcx, OverflowOp::Mul, sp, lhs_t, lhs, rhs); + bcx = newbcx; + res + } } ast::BiDiv => { if is_float { @@ -2283,3 +2296,107 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, DatumBlock { bcx: bcx, datum: datum } } } + +enum OverflowOp { + Add, + Sub, + Mul, +} + +impl OverflowOp { + fn to_intrinsic_name(&self, tcx: &ty::ctxt, ty: Ty) -> &'static str { + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + use middle::ty::{ty_int, ty_uint}; + + let new_sty = match ty.sty { + ty_int(TyIs(_)) => match &tcx.sess.target.target.target_pointer_width[] { + "32" => ty_int(TyI32), + "64" => ty_int(TyI64), + _ => panic!("unsupported target word size") + }, + ty_uint(TyUs(_)) => match &tcx.sess.target.target.target_pointer_width[] { + "32" => ty_uint(TyU32), + "64" => ty_uint(TyU64), + _ => panic!("unsupported target word size") + }, + ref t @ ty_uint(_) | ref t @ ty_int(_) => t.clone(), + _ => panic!("tried to get overflow intrinsic for non-int type") + }; + + match *self { + OverflowOp::Add => match new_sty { + ty_int(TyI8) => "llvm.sadd.with.overflow.i8", + ty_int(TyI16) => "llvm.sadd.with.overflow.i16", + ty_int(TyI32) => "llvm.sadd.with.overflow.i32", + ty_int(TyI64) => "llvm.sadd.with.overflow.i64", + + ty_uint(TyU8) => "llvm.uadd.with.overflow.i8", + ty_uint(TyU16) => "llvm.uadd.with.overflow.i16", + ty_uint(TyU32) => "llvm.uadd.with.overflow.i32", + ty_uint(TyU64) => "llvm.uadd.with.overflow.i64", + + _ => unreachable!(), + }, + OverflowOp::Sub => match new_sty { + ty_int(TyI8) => "llvm.ssub.with.overflow.i8", + ty_int(TyI16) => "llvm.ssub.with.overflow.i16", + ty_int(TyI32) => "llvm.ssub.with.overflow.i32", + ty_int(TyI64) => "llvm.ssub.with.overflow.i64", + + ty_uint(TyU8) => "llvm.usub.with.overflow.i8", + ty_uint(TyU16) => "llvm.usub.with.overflow.i16", + ty_uint(TyU32) => "llvm.usub.with.overflow.i32", + ty_uint(TyU64) => "llvm.usub.with.overflow.i64", + + _ => unreachable!(), + }, + OverflowOp::Mul => match new_sty { + ty_int(TyI8) => "llvm.smul.with.overflow.i8", + ty_int(TyI16) => "llvm.smul.with.overflow.i16", + ty_int(TyI32) => "llvm.smul.with.overflow.i32", + ty_int(TyI64) => "llvm.smul.with.overflow.i64", + + ty_uint(TyU8) => "llvm.umul.with.overflow.i8", + ty_uint(TyU16) => "llvm.umul.with.overflow.i16", + ty_uint(TyU32) => "llvm.umul.with.overflow.i32", + ty_uint(TyU64) => "llvm.umul.with.overflow.i64", + + _ => unreachable!(), + }, + } + } +} + + +fn with_overflow_check<'a, 'b>(bcx: Block<'a, 'b>, oop: OverflowOp, sp: codemap::Span, + lhs_t: Ty, lhs: ValueRef, rhs: ValueRef) -> (Block<'a, 'b>, ValueRef) { + if bcx.unreachable.get() { return (bcx, _Undef(lhs)); } + if bcx.ccx().check_overflow() { + let name = oop.to_intrinsic_name(bcx.tcx(), lhs_t); + let llfn = bcx.ccx().get_intrinsic(&name); + + let val = Call(bcx, llfn, &[lhs, rhs], None); + let result = ExtractValue(bcx, val, 0); // iN operation result + let overflow = ExtractValue(bcx, val, 1); // i1 "did it overflow?" + + let cond = ICmp(bcx, llvm::IntEQ, overflow, C_integral(Type::i1(bcx.ccx()), 1, false)); + + let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1"); + Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)], None); + + let bcx = + base::with_cond(bcx, cond, |bcx| + controlflow::trans_fail(bcx, sp, + InternedString::new("arithmetic operation overflowed"))); + + (bcx, result) + } else { + let res = match oop { + OverflowOp::Add => Add(bcx, lhs, rhs), + OverflowOp::Sub => Sub(bcx, lhs, rhs), + OverflowOp::Mul => Mul(bcx, lhs, rhs), + }; + (bcx, res) + } +} diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index b22c7f763f035..7880683b72abb 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -506,6 +506,10 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, with_overflow_intrinsic(bcx, "llvm.umul.with.overflow.i64", ret_ty, llargs[0], llargs[1]), + (_, "overflowing_add") => Add(bcx, llargs[0], llargs[1]), + (_, "overflowing_sub") => Sub(bcx, llargs[0], llargs[1]), + (_, "overflowing_mul") => Mul(bcx, llargs[0], llargs[1]), + (_, "return_address") => { if !fcx.caller_expects_out_pointer { tcx.sess.span_err(call_info.span, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1d184131dede3..faf9aa4ad1629 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4826,6 +4826,7 @@ pub fn check_enum_variants(ccx: &CrateCtxt, id: ast::NodeId, hint: attr::ReprAttr) -> Vec>> { + use std::num::Int; let rty = ty::node_id_to_type(ccx.tcx, id); let mut variants: Vec> = Vec::new(); @@ -4837,7 +4838,13 @@ pub fn check_enum_variants(ccx: &CrateCtxt, // If the discriminant value is specified explicitly in the enum check whether the // initialization expression is valid, otherwise use the last value plus one. let mut current_disr_val = match prev_disr_val { - Some(prev_disr_val) => prev_disr_val + 1, + Some(prev_disr_val) => { + if let Some(v) = prev_disr_val.checked_add(1) { + v + } else { + ty::INITIAL_DISCRIMINANT_VALUE + } + } None => ty::INITIAL_DISCRIMINANT_VALUE }; @@ -5763,6 +5770,9 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { (0, vec!(tcx.types.u64, tcx.types.u64), ty::mk_tup(tcx, vec!(tcx.types.u64, tcx.types.bool))), + "overflowing_add" | "overflowing_sub" | "overflowing_mul" => + (1, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 0)), + "return_address" => (0, vec![], ty::mk_imm_ptr(tcx, tcx.types.u8)), "assume" => (0, vec![tcx.types.bool], ty::mk_nil(tcx)), diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index c3bdfbb12d88d..31bfc585b066c 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -314,6 +314,13 @@ fn search_hashed(table: M, M: Deref>, F: FnMut(&K) -> bool, { + // This is the only function where capacity can be zero. To avoid + // undefined behaviour when Bucket::new gets the raw bucket in this + // case, immediately return the appropriate search result. + if table.capacity() == 0 { + return TableRef(table); + } + let size = table.size(); let mut probe = Bucket::new(table, hash); let ib = probe.index(); diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 456f3763b3916..1515ca085e443 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -221,6 +221,9 @@ impl>> Bucket { } pub fn at_index(table: M, ib_index: uint) -> Bucket { + // if capacity is 0, then the RawBucket will be populated with bogus pointers. + // This is an uncommon case though, so avoid it in release builds. + debug_assert!(table.capacity() > 0, "Table should have capacity at this point"); let ib_index = ib_index & (table.capacity() - 1); Bucket { raw: unsafe { @@ -365,10 +368,11 @@ impl>> FullBucket { /// In the cited blog posts above, this is called the "distance to /// initial bucket", or DIB. Also known as "probe count". pub fn distance(&self) -> uint { + use core::num::wrapping::WrappingOps; // Calculates the distance one has to travel when going from // `hash mod capacity` onwards to `idx mod capacity`, wrapping around // if the destination is not reached before the end of the table. - (self.idx - self.hash().inspect() as uint) & (self.table.capacity() - 1) + (self.idx.wrapping_sub(self.hash().inspect() as uint)) & (self.table.capacity() - 1) } #[inline] diff --git a/src/libstd/num/mod.rs b/src/libstd/num/mod.rs index e804408b4d0e2..c05ee46361235 100644 --- a/src/libstd/num/mod.rs +++ b/src/libstd/num/mod.rs @@ -30,6 +30,7 @@ pub use core::num::{from_uint, from_u8, from_u16, from_u32, from_u64}; pub use core::num::{from_f32, from_f64}; pub use core::num::{FromStrRadix, from_str_radix}; pub use core::num::{FpCategory}; +pub use core::num::wrapping; use option::Option; diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs index d9c942c018514..522389e97c92c 100644 --- a/src/libstd/prelude/v1.rs +++ b/src/libstd/prelude/v1.rs @@ -46,3 +46,5 @@ #[doc(no_inline)] pub use io::{Buffer, Writer, Reader, Seek, BufferPrelude}; // NB: remove when range syntax lands #[doc(no_inline)] pub use iter::range; + +#[doc(no_inline)] pub use num::wrapping::{Wrapping, WrappingOps}; diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs index 616390467f06f..76f4850a9c1c1 100644 --- a/src/libsyntax/ext/deriving/encodable.rs +++ b/src/libsyntax/ext/deriving/encodable.rs @@ -232,25 +232,25 @@ fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span, let encoder = cx.expr_ident(trait_span, blkarg); let emit_variant_arg = cx.ident_of("emit_enum_variant_arg"); let mut stmts = Vec::new(); - let last = fields.len() - 1; - for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() { - let enc = cx.expr_method_call(span, self_.clone(), - encode, vec!(blkencoder.clone())); - let lambda = cx.lambda_expr_1(span, enc, blkarg); - let call = cx.expr_method_call(span, blkencoder.clone(), - emit_variant_arg, - vec!(cx.expr_uint(span, i), - lambda)); - let call = if i != last { - cx.expr_try(span, call) - } else { - cx.expr(span, ExprRet(Some(call))) - }; - stmts.push(cx.stmt_expr(call)); - } - - // enums with no fields need to return Ok() - if stmts.len() == 0 { + if fields.len() > 0 { + let last = fields.len() - 1; + for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() { + let enc = cx.expr_method_call(span, self_.clone(), + encode, vec!(blkencoder.clone())); + let lambda = cx.lambda_expr_1(span, enc, blkarg); + let call = cx.expr_method_call(span, blkencoder.clone(), + emit_variant_arg, + vec!(cx.expr_uint(span, i), + lambda)); + let call = if i != last { + cx.expr_try(span, call) + } else { + cx.expr(span, ExprRet(Some(call))) + }; + stmts.push(cx.stmt_expr(call)); + } + } else { + // enums with no fields need to return Ok() let ret_ok = cx.expr(trait_span, ExprRet(Some(cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![]))))); diff --git a/src/test/run-fail/overflowing-add.rs b/src/test/run-fail/overflowing-add.rs new file mode 100644 index 0000000000000..c3e41110d20da --- /dev/null +++ b/src/test/run-fail/overflowing-add.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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. + +// error-pattern:thread '
' panicked at 'arithmatic operation overflowed' + +fn main() { + let x = 200u8 + 200u8 + 200u8; +} diff --git a/src/test/run-fail/overflowing-mul.rs b/src/test/run-fail/overflowing-mul.rs new file mode 100644 index 0000000000000..bf7a9d0758651 --- /dev/null +++ b/src/test/run-fail/overflowing-mul.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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. + +// error-pattern:thread '
' panicked at 'arithmatic operation overflowed' + +fn main() { + let x = 200u8 + 4u8; +} diff --git a/src/test/run-fail/overflowing-sub.rs b/src/test/run-fail/overflowing-sub.rs new file mode 100644 index 0000000000000..961b36d322cdb --- /dev/null +++ b/src/test/run-fail/overflowing-sub.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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. + +// error-pattern:thread '
' panicked at 'arithmatic operation overflowed' + +fn main() { + let x = 42u8 - 43u8; +}