From 378805c87ea82761d6206659cf2755b323d47365 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Tue, 6 Jan 2015 00:56:30 -0500 Subject: [PATCH 1/4] rustc: implement arithmatic overflow checking Adds overflow checking to integer addition, multiplication, and subtraction when `-Z force-overflow-checks` is true, or if `--cfg ndebug` is not passed to the compiler. On overflow, it panics with `arithmatic operation overflowed`. Also adds `overflowing_add`, `overflowing_sub`, and `overflowing_mul` intrinsics for doing unchecked arithmatic. [breaking-change] --- src/libcore/intrinsics.rs | 10 +++ src/librustc/session/config.rs | 101 +++++++++++++++++++++ src/librustc_trans/trans/base.rs | 6 +- src/librustc_trans/trans/context.rs | 9 +- src/librustc_trans/trans/expr.rs | 125 +++++++++++++++++++++++++- src/librustc_trans/trans/intrinsic.rs | 4 + src/test/run-fail/overflowing-add.rs | 15 ++++ src/test/run-fail/overflowing-mul.rs | 15 ++++ src/test/run-fail/overflowing-sub.rs | 15 ++++ 9 files changed, 294 insertions(+), 6 deletions(-) create mode 100644 src/test/run-fail/overflowing-add.rs create mode 100644 src/test/run-fail/overflowing-mul.rs create mode 100644 src/test/run-fail/overflowing-sub.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 5924d515dda59..6ab86807d51d2 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -547,6 +547,16 @@ extern "rust-intrinsic" { pub fn u64_mul_with_overflow(x: u64, y: u64) -> (u64, bool); } +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; +} + +#[cfg(not(stage0))] /// `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/librustc/session/config.rs b/src/librustc/session/config.rs index d9bb1d769bfbe..1bb3173f5523b 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -257,6 +257,107 @@ pub enum CrateType { CrateTypeStaticlib, } +macro_rules! debugging_opts { + ([ $opt:ident ] $cnt:expr ) => ( + pub const $opt: u64 = 1 << $cnt; + ); + ([ $opt:ident, $($rest:ident),* ] $cnt:expr ) => ( + pub const $opt: u64 = 1 << $cnt; + debugging_opts! { [ $($rest),* ] $cnt + 1 } + ) +} + +debugging_opts! { + [ + VERBOSE, + TIME_PASSES, + COUNT_LLVM_INSNS, + TIME_LLVM_PASSES, + TRANS_STATS, + ASM_COMMENTS, + NO_VERIFY, + BORROWCK_STATS, + NO_LANDING_PADS, + DEBUG_LLVM, + COUNT_TYPE_SIZES, + META_STATS, + GC, + PRINT_LINK_ARGS, + PRINT_LLVM_PASSES, + AST_JSON, + AST_JSON_NOEXPAND, + LS, + SAVE_ANALYSIS, + PRINT_MOVE_FRAGMENTS, + FLOWGRAPH_PRINT_LOANS, + FLOWGRAPH_PRINT_MOVES, + FLOWGRAPH_PRINT_ASSIGNS, + FLOWGRAPH_PRINT_ALL, + PRINT_REGION_GRAPH, + PARSE_ONLY, + NO_TRANS, + NO_ANALYSIS, + UNSTABLE_OPTIONS, + PRINT_ENUM_SIZES, + FORCE_OVERFLOW_CHECKS, + FORCE_NO_OVERFLOW_CHECKS, + ] + 0 +} + +pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> { + vec![("verbose", "in general, enable more debug printouts", VERBOSE), + ("time-passes", "measure time of each rustc pass", TIME_PASSES), + ("count-llvm-insns", "count where LLVM \ + instrs originate", COUNT_LLVM_INSNS), + ("time-llvm-passes", "measure time of each LLVM pass", + TIME_LLVM_PASSES), + ("trans-stats", "gather trans statistics", TRANS_STATS), + ("asm-comments", "generate comments into the assembly (may change behavior)", + ASM_COMMENTS), + ("no-verify", "skip LLVM verification", NO_VERIFY), + ("borrowck-stats", "gather borrowck statistics", BORROWCK_STATS), + ("no-landing-pads", "omit landing pads for unwinding", + NO_LANDING_PADS), + ("debug-llvm", "enable debug output from LLVM", DEBUG_LLVM), + ("count-type-sizes", "count the sizes of aggregate types", + COUNT_TYPE_SIZES), + ("meta-stats", "gather metadata statistics", META_STATS), + ("print-link-args", "Print the arguments passed to the linker", + PRINT_LINK_ARGS), + ("gc", "Garbage collect shared data (experimental)", GC), + ("print-llvm-passes", + "Prints the llvm optimization passes being run", + PRINT_LLVM_PASSES), + ("ast-json", "Print the AST as JSON and halt", AST_JSON), + ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND), + ("ls", "List the symbols defined by a library crate", LS), + ("save-analysis", "Write syntax and type analysis information \ + in addition to normal output", SAVE_ANALYSIS), + ("print-move-fragments", "Print out move-fragment data for every fn", + PRINT_MOVE_FRAGMENTS), + ("flowgraph-print-loans", "Include loan analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_LOANS), + ("flowgraph-print-moves", "Include move analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_MOVES), + ("flowgraph-print-assigns", "Include assignment analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS), + ("flowgraph-print-all", "Include all dataflow analysis data in \ + --pretty flowgraph output", FLOWGRAPH_PRINT_ALL), + ("print-region-graph", "Prints region inference graph. \ + Use with RUST_REGION_GRAPH=help for more info", + PRINT_REGION_GRAPH), + ("parse-only", "Parse only; do not compile, assemble, or link", PARSE_ONLY), + ("no-trans", "Run all passes except translation; no output", NO_TRANS), + ("no-analysis", "Parse and expand the source, but run no analysis and", + NO_TRANS), + ("unstable-options", "Adds unstable command line options to rustc interface", + UNSTABLE_OPTIONS), + ("print-enum-sizes", "Print the size of enums and their variants", PRINT_ENUM_SIZES), + ("force-overflow-checks", "Force arithmatic overflow checking", FORCE_OVERFLOW_CHECKS), + ("force-no-overflow-checks", "Force arithmatic overflow checking", FORCE_NO_OVERFLOW_CHECKS), + ] +} #[derive(Clone)] pub enum Passes { diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 88ce36a710a0c..ca1a6ebf40984 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -3111,6 +3111,9 @@ 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 = tcx.sess.opts.debugging_opts & config::FORCE_OVERFLOW_CHECKS != 0 + || !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 +3141,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..ffa3164d26ab8 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(TyI) => match tcx.sess.target.target.target_word_size[] { + "32" => ty_int(TyI32), + "64" => ty_int(TyI64), + _ => panic!("unsupported target word size") + }, + ty_uint(TyU) => match tcx.sess.target.target.target_word_size[] { + "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..2a70f89b6512c 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/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; +} From c698a1b3a716f7a512b55134422e68c7de3e8f14 Mon Sep 17 00:00:00 2001 From: James Miller Date: Thu, 8 Jan 2015 09:46:56 +1300 Subject: [PATCH 2/4] Add Wrapping and WrappingOps implementations Adds a Wrapping wrapper type and a WrappingOps trait for allowing people to explictly opt-in to wrapping integer arithmetic. This commit currently disables overflow checking in the build due to cases of expected overflow triggering the panic. --- mk/main.mk | 5 +- src/libcore/intrinsics.rs | 3 +- src/libcore/num/mod.rs | 2 + src/libcore/num/wrapping.rs | 90 +++++++++++++++++++++++++++ src/libcore/ops.rs | 1 + src/libcore/prelude.rs | 2 + src/librand/isaac.rs | 1 + src/librustc/session/config.rs | 6 +- src/librustc_trans/trans/base.rs | 5 +- src/librustc_trans/trans/intrinsic.rs | 6 +- src/librustc_typeck/check/mod.rs | 3 + 11 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 src/libcore/num/wrapping.rs diff --git a/mk/main.mk b/mk/main.mk index 99aecc34be973..98651af5682e5 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -157,8 +157,9 @@ endif # have to worry about the distribution of one file (with its native dynamic # dependencies) RUSTFLAGS_STAGE0 += -C prefer-dynamic -RUSTFLAGS_STAGE1 += -C prefer-dynamic -RUST_LIB_FLAGS_ST2 += -C prefer-dynamic +# FIXME: Remove the -Z force-no-overflow-checks flags +RUSTFLAGS_STAGE1 += -C prefer-dynamic -Z force-no-overflow-checks +RUST_LIB_FLAGS_ST2 += -C prefer-dynamic -Z force-no-overflow-checks RUST_LIB_FLAGS_ST3 += -C prefer-dynamic # Landing pads require a lot of codegen. We can get through bootstrapping faster diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 6ab86807d51d2..05fba9eea0e25 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -547,6 +547,7 @@ extern "rust-intrinsic" { pub fn u64_mul_with_overflow(x: u64, y: u64) -> (u64, bool); } +#[cfg(not(stage1))] 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; @@ -556,8 +557,6 @@ extern "rust-intrinsic" { pub fn overflowing_mul(a: T, b: T) -> T; } -#[cfg(not(stage0))] - /// `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 // middle/lang_items.rs diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 57415f4331d6d..68f695cd4153e 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -29,6 +29,8 @@ use option::Option; use option::Option::{Some, None}; use str::{FromStr, StrExt}; +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..a0e6d0821d51a --- /dev/null +++ b/src/libcore/num/wrapping.rs @@ -0,0 +1,90 @@ +#![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)] +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)) + } +} 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/librand/isaac.rs b/src/librand/isaac.rs index 84328360ce324..0db14d5af192e 100644 --- a/src/librand/isaac.rs +++ b/src/librand/isaac.rs @@ -307,6 +307,7 @@ impl Isaac64Rng { let mut $var = 0x9e3779b97f4a7c13; ) } + init!(a); init!(b); init!(c); init!(d); init!(e); init!(f); init!(g); init!(h); diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 1bb3173f5523b..eb17c51fdd313 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -300,7 +300,7 @@ debugging_opts! { UNSTABLE_OPTIONS, PRINT_ENUM_SIZES, FORCE_OVERFLOW_CHECKS, - FORCE_NO_OVERFLOW_CHECKS, + FORCE_NO_OVERFLOW_CHECKS ] 0 } @@ -354,8 +354,8 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> { ("unstable-options", "Adds unstable command line options to rustc interface", UNSTABLE_OPTIONS), ("print-enum-sizes", "Print the size of enums and their variants", PRINT_ENUM_SIZES), - ("force-overflow-checks", "Force arithmatic overflow checking", FORCE_OVERFLOW_CHECKS), - ("force-no-overflow-checks", "Force arithmatic overflow checking", FORCE_NO_OVERFLOW_CHECKS), + ("force-overflow-checks", "Force arithmetic overflow checking on", FORCE_OVERFLOW_CHECKS), + ("force-no-overflow-checks", "Force arithmetic overflow checking off", FORCE_NO_OVERFLOW_CHECKS), ] } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index ca1a6ebf40984..e19d95f34dc27 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -3111,8 +3111,9 @@ 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 = tcx.sess.opts.debugging_opts & config::FORCE_OVERFLOW_CHECKS != 0 - || !attr::contains_name(krate.config[], "ndebug"); + let check_overflow = tcx.sess.opts.debugging_opts & config::FORCE_NO_OVERFLOW_CHECKS == 0 + && (tcx.sess.opts.debugging_opts & config::FORCE_OVERFLOW_CHECKS != 0 + || !attr::contains_name(krate.config[], "ndebug")); // Before we touch LLVM, make sure that multithreading is enabled. unsafe { diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 2a70f89b6512c..7880683b72abb 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -506,9 +506,9 @@ 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]) + (_, "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 { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1d184131dede3..eed50773b5e88 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5763,6 +5763,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)), From 26901072965a9aaa5a86c542d20d9bb3a00f05b4 Mon Sep 17 00:00:00 2001 From: James Miller Date: Fri, 9 Jan 2015 16:10:57 +1300 Subject: [PATCH 3/4] Fix overflow errors Many of the core rust libraries have places that rely on integer wrapping behaviour. These places have been altered to use the wrapping_* methods: * core::hash::sip - A number of macros * core::str - The `maximal_suffix` method in `TwoWaySearcher` * rustc::util::nodemap - Implementation of FnvHash * rustc_back::sha2 - A number of macros and other places * rand::isaac - Isaac64Rng, changed to use the Wrapping helper type Some places had "benign" underflow. This is when underflow or overflow occurs, but the unspecified value is not used due to other conditions. * collections::bit::Bitv - underflow when `self.nbits` is zero. * collections::hash::{map,table} - Underflow when searching an empty table. Did cause undefined behaviour in this case due to an out-of-bounds ptr::offset based on the underflowed index. However the resulting pointers would never be read from. * syntax::ext::deriving::encodable - Underflow when calculating the index of the last field in a variant with no fields. These cases were altered to avoid the underflow, often by moving the underflowing operation to a place where underflow could not happen. There was one case that relied on the fact that unsigned arithmetic and two's complement arithmetic are identical with wrapping semantics. This was changed to use the wrapping_* methods. Finally, the calculation of variant discriminants could overflow if the preceeding discriminant was `U64_MAX`. The logic in `rustc::middle::ty` for this was altered to avoid the overflow completely, while the remaining places were changed to use wrapping methods. This is because `rustc::middle::ty::enum_variants` now throws an error when the calculated discriminant value overflows a `u64`. This behaviour can be triggered by the following code: ``` enum Foo { A = U64_MAX, B } ``` This commit also implements the remaining integer operators for Wrapped. --- mk/main.mk | 5 +- src/libcollections/bit.rs | 6 +- src/libcore/hash/sip.rs | 10 ++-- src/libcore/intrinsics.rs | 2 +- src/libcore/num/mod.rs | 1 + src/libcore/num/wrapping.rs | 55 +++++++++++++++++- src/libcore/str/mod.rs | 9 +-- src/librand/isaac.rs | 74 ++++++++++++------------- src/librustc/metadata/decoder.rs | 2 +- src/librustc/metadata/encoder.rs | 2 +- src/librustc/middle/astencode.rs | 4 +- src/librustc/middle/ty.rs | 20 +++++-- src/librustc/util/nodemap.rs | 2 +- src/librustc_back/sha2.rs | 28 +++++----- src/librustc_typeck/check/mod.rs | 9 ++- src/libstd/collections/hash/map.rs | 7 +++ src/libstd/collections/hash/table.rs | 6 +- src/libstd/num/mod.rs | 1 + src/libstd/prelude/v1.rs | 2 + src/libsyntax/ext/deriving/encodable.rs | 38 ++++++------- 20 files changed, 186 insertions(+), 97 deletions(-) diff --git a/mk/main.mk b/mk/main.mk index 98651af5682e5..99aecc34be973 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -157,9 +157,8 @@ endif # have to worry about the distribution of one file (with its native dynamic # dependencies) RUSTFLAGS_STAGE0 += -C prefer-dynamic -# FIXME: Remove the -Z force-no-overflow-checks flags -RUSTFLAGS_STAGE1 += -C prefer-dynamic -Z force-no-overflow-checks -RUST_LIB_FLAGS_ST2 += -C prefer-dynamic -Z force-no-overflow-checks +RUSTFLAGS_STAGE1 += -C prefer-dynamic +RUST_LIB_FLAGS_ST2 += -C prefer-dynamic RUST_LIB_FLAGS_ST3 += -C prefer-dynamic # Landing pads require a lot of codegen. We can get through bootstrapping faster 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 05fba9eea0e25..ac5727b1b8955 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -547,7 +547,7 @@ extern "rust-intrinsic" { pub fn u64_mul_with_overflow(x: u64, y: u64) -> (u64, bool); } -#[cfg(not(stage1))] +#[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; diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 68f695cd4153e..fdd2a86e87b5e 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -29,6 +29,7 @@ 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. diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index a0e6d0821d51a..3ac1fa12ca8d4 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs @@ -59,7 +59,7 @@ macro_rules! wrapping_impl { wrapping_impl! { uint u8 u16 u32 u64 int i8 i16 i32 i64 } -#[derive(PartialEq,Eq,PartialOrd,Ord,Clone)] +#[derive(PartialEq,Eq,PartialOrd,Ord,Clone,Copy)] pub struct Wrapping(pub T); impl Add for Wrapping { @@ -88,3 +88,56 @@ impl Mul for 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/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 0db14d5af192e..e9113a85f2f1e 100644 --- a/src/librand/isaac.rs +++ b/src/librand/isaac.rs @@ -304,7 +304,7 @@ impl Isaac64Rng { fn init(&mut self, use_rsl: bool) { macro_rules! init { ($var:ident) => ( - let mut $var = 0x9e3779b97f4a7c13; + let mut $var = Wrapping(0x9e3779b97f4a7c13); ) } @@ -313,14 +313,14 @@ impl Isaac64Rng { 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; }} } @@ -332,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; } }} } @@ -350,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; } } @@ -364,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 { @@ -384,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; } }} } @@ -402,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; } }} } @@ -420,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..a087d94c85219 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/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_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index eed50773b5e88..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 }; 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![]))))); From afc2c1662c09818c55e3fb95de41072087a8976c Mon Sep 17 00:00:00 2001 From: James Miller Date: Fri, 9 Jan 2015 18:06:45 +1300 Subject: [PATCH 4/4] Implement parse_opt_bool better During my clean-up of rebase errors, I took the opportunity to implement parse_opt_bool so that it isn't identical to parse_bool wrapped in `Some`. parse_opt_bool considers no value to be true, a value of 'y', 'yes' or 'on' to be true and 'n', 'no' or 'off' to be false. All other values are an error. --- src/libcore/num/wrapping.rs | 9 +++ src/librustc/middle/ty.rs | 2 +- src/librustc/session/config.rs | 121 +++++-------------------------- src/librustc_trans/trans/base.rs | 8 +- src/librustc_trans/trans/expr.rs | 4 +- 5 files changed, 34 insertions(+), 110 deletions(-) diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index 3ac1fa12ca8d4..de971ad001c38 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs @@ -1,3 +1,12 @@ +// 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::*; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index a087d94c85219..446fed05a24ee 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5364,7 +5364,7 @@ pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId) } else { cx.sess.span_err( variant.span, - format!("Discriminant overflowed!")[]); + &format!("Discriminant overflowed!")[]); } } else { discriminant = INITIAL_DISCRIMINANT_VALUE; diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index eb17c51fdd313..172268996d5ff 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -257,108 +257,6 @@ pub enum CrateType { CrateTypeStaticlib, } -macro_rules! debugging_opts { - ([ $opt:ident ] $cnt:expr ) => ( - pub const $opt: u64 = 1 << $cnt; - ); - ([ $opt:ident, $($rest:ident),* ] $cnt:expr ) => ( - pub const $opt: u64 = 1 << $cnt; - debugging_opts! { [ $($rest),* ] $cnt + 1 } - ) -} - -debugging_opts! { - [ - VERBOSE, - TIME_PASSES, - COUNT_LLVM_INSNS, - TIME_LLVM_PASSES, - TRANS_STATS, - ASM_COMMENTS, - NO_VERIFY, - BORROWCK_STATS, - NO_LANDING_PADS, - DEBUG_LLVM, - COUNT_TYPE_SIZES, - META_STATS, - GC, - PRINT_LINK_ARGS, - PRINT_LLVM_PASSES, - AST_JSON, - AST_JSON_NOEXPAND, - LS, - SAVE_ANALYSIS, - PRINT_MOVE_FRAGMENTS, - FLOWGRAPH_PRINT_LOANS, - FLOWGRAPH_PRINT_MOVES, - FLOWGRAPH_PRINT_ASSIGNS, - FLOWGRAPH_PRINT_ALL, - PRINT_REGION_GRAPH, - PARSE_ONLY, - NO_TRANS, - NO_ANALYSIS, - UNSTABLE_OPTIONS, - PRINT_ENUM_SIZES, - FORCE_OVERFLOW_CHECKS, - FORCE_NO_OVERFLOW_CHECKS - ] - 0 -} - -pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> { - vec![("verbose", "in general, enable more debug printouts", VERBOSE), - ("time-passes", "measure time of each rustc pass", TIME_PASSES), - ("count-llvm-insns", "count where LLVM \ - instrs originate", COUNT_LLVM_INSNS), - ("time-llvm-passes", "measure time of each LLVM pass", - TIME_LLVM_PASSES), - ("trans-stats", "gather trans statistics", TRANS_STATS), - ("asm-comments", "generate comments into the assembly (may change behavior)", - ASM_COMMENTS), - ("no-verify", "skip LLVM verification", NO_VERIFY), - ("borrowck-stats", "gather borrowck statistics", BORROWCK_STATS), - ("no-landing-pads", "omit landing pads for unwinding", - NO_LANDING_PADS), - ("debug-llvm", "enable debug output from LLVM", DEBUG_LLVM), - ("count-type-sizes", "count the sizes of aggregate types", - COUNT_TYPE_SIZES), - ("meta-stats", "gather metadata statistics", META_STATS), - ("print-link-args", "Print the arguments passed to the linker", - PRINT_LINK_ARGS), - ("gc", "Garbage collect shared data (experimental)", GC), - ("print-llvm-passes", - "Prints the llvm optimization passes being run", - PRINT_LLVM_PASSES), - ("ast-json", "Print the AST as JSON and halt", AST_JSON), - ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND), - ("ls", "List the symbols defined by a library crate", LS), - ("save-analysis", "Write syntax and type analysis information \ - in addition to normal output", SAVE_ANALYSIS), - ("print-move-fragments", "Print out move-fragment data for every fn", - PRINT_MOVE_FRAGMENTS), - ("flowgraph-print-loans", "Include loan analysis data in \ - --pretty flowgraph output", FLOWGRAPH_PRINT_LOANS), - ("flowgraph-print-moves", "Include move analysis data in \ - --pretty flowgraph output", FLOWGRAPH_PRINT_MOVES), - ("flowgraph-print-assigns", "Include assignment analysis data in \ - --pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS), - ("flowgraph-print-all", "Include all dataflow analysis data in \ - --pretty flowgraph output", FLOWGRAPH_PRINT_ALL), - ("print-region-graph", "Prints region inference graph. \ - Use with RUST_REGION_GRAPH=help for more info", - PRINT_REGION_GRAPH), - ("parse-only", "Parse only; do not compile, assemble, or link", PARSE_ONLY), - ("no-trans", "Run all passes except translation; no output", NO_TRANS), - ("no-analysis", "Parse and expand the source, but run no analysis and", - NO_TRANS), - ("unstable-options", "Adds unstable command line options to rustc interface", - UNSTABLE_OPTIONS), - ("print-enum-sizes", "Print the size of enums and their variants", PRINT_ENUM_SIZES), - ("force-overflow-checks", "Force arithmetic overflow checking on", FORCE_OVERFLOW_CHECKS), - ("force-no-overflow-checks", "Force arithmetic overflow checking off", FORCE_NO_OVERFLOW_CHECKS), - ] -} - #[derive(Clone)] pub enum Passes { SomePasses(Vec), @@ -449,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"); @@ -480,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 } } } @@ -683,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_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index e19d95f34dc27..76c2194e07b31 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -3111,9 +3111,11 @@ 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 = tcx.sess.opts.debugging_opts & config::FORCE_NO_OVERFLOW_CHECKS == 0 - && (tcx.sess.opts.debugging_opts & config::FORCE_OVERFLOW_CHECKS != 0 - || !attr::contains_name(krate.config[], "ndebug")); + 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 { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index ffa3164d26ab8..dd0d4814ca0bb 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -2310,12 +2310,12 @@ impl OverflowOp { use middle::ty::{ty_int, ty_uint}; let new_sty = match ty.sty { - ty_int(TyI) => match tcx.sess.target.target.target_word_size[] { + 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(TyU) => match tcx.sess.target.target.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")