rollup merge of #23945: pnkfelix/gate-u-negate

Feature-gate  unsigned unary negate.

Discussed in weekly meeting here: https://github.com/rust-lang/meeting-minutes/blob/master/weekly-meetings/2015-03-31.md#feature-gate--expr

and also in the internals thread here: http://internals.rust-lang.org/t/forbid-unsigned-integer/752
This commit is contained in:
Alex Crichton 2015-04-01 18:36:21 -07:00
commit 9edbf42a34
32 changed files with 146 additions and 48 deletions

View file

@ -1135,7 +1135,7 @@ impl Iterator for ElementSwaps {
// #[inline]
fn next(&mut self) -> Option<(usize, usize)> {
fn new_pos_wrapping(i: usize, s: Direction) -> usize {
i.wrapping_add(match s { Pos => 1, Neg => -1 })
i.wrapping_add(match s { Pos => 1, Neg => !0 /* aka -1 */ })
}
fn new_pos(i: usize, s: Direction) -> usize {

View file

@ -1089,7 +1089,7 @@ fn test_bytes_set_memory() {
#[should_panic]
fn test_overflow_does_not_cause_segfault() {
let mut v = vec![];
v.reserve_exact(-1);
v.reserve_exact(!0);
v.push(1);
v.push(2);
}
@ -1098,7 +1098,7 @@ fn test_overflow_does_not_cause_segfault() {
#[should_panic]
fn test_overflow_does_not_cause_segfault_managed() {
let mut v = vec![Rc::new(1)];
v.reserve_exact(-1);
v.reserve_exact(!0);
v.push(Rc::new(2));
}

View file

@ -161,7 +161,7 @@ pub enum Ordering {
AtomicUsize { v: UnsafeCell { value: 0, } };
// NB: Needs to be -1 (0b11111111...) to make fetch_nand work correctly
const UINT_TRUE: usize = -1;
const UINT_TRUE: usize = !0;
impl AtomicBool {
/// Creates a new `AtomicBool`.

View file

@ -287,7 +287,7 @@ pub enum BorrowState {
// (will not outgrow its range since `usize` is the size of the address space)
type BorrowFlag = usize;
const UNUSED: BorrowFlag = 0;
const WRITING: BorrowFlag = -1;
const WRITING: BorrowFlag = !0;
impl<T> RefCell<T> {
/// Creates a new `RefCell` containing `value`.

View file

@ -516,7 +516,7 @@ fn one() -> $T { 1 }
fn min_value() -> $T { 0 }
#[inline]
fn max_value() -> $T { -1 }
fn max_value() -> $T { !0 }
#[inline]
fn count_ones(self) -> u32 {

View file

@ -30,7 +30,7 @@
use intrinsics::{i32_mul_with_overflow, u32_mul_with_overflow};
use intrinsics::{i64_mul_with_overflow, u64_mul_with_overflow};
use ::{i8,i16,i32,i64,u8,u16,u32,u64};
use ::{i8,i16,i32,i64};
#[unstable(feature = "core", reason = "may be removed, renamed, or relocated")]
#[deprecated(since = "1.0.0", reason = "moved to inherent methods")]
@ -206,7 +206,7 @@ mod shift_max {
pub const u64: u32 = i64;
}
macro_rules! overflowing_impl {
macro_rules! signed_overflowing_impl {
($($t:ident)*) => ($(
impl OverflowingOps for $t {
#[inline(always)]
@ -259,7 +259,53 @@ fn overflowing_shr(self, rhs: u32) -> ($t, bool) {
)*)
}
overflowing_impl! { u8 u16 u32 u64 i8 i16 i32 i64 }
macro_rules! unsigned_overflowing_impl {
($($t:ident)*) => ($(
impl OverflowingOps for $t {
#[inline(always)]
fn overflowing_add(self, rhs: $t) -> ($t, bool) {
unsafe {
concat_idents!($t, _add_with_overflow)(self, rhs)
}
}
#[inline(always)]
fn overflowing_sub(self, rhs: $t) -> ($t, bool) {
unsafe {
concat_idents!($t, _sub_with_overflow)(self, rhs)
}
}
#[inline(always)]
fn overflowing_mul(self, rhs: $t) -> ($t, bool) {
unsafe {
concat_idents!($t, _mul_with_overflow)(self, rhs)
}
}
#[inline(always)]
fn overflowing_div(self, rhs: $t) -> ($t, bool) {
(self/rhs, false)
}
#[inline(always)]
fn overflowing_rem(self, rhs: $t) -> ($t, bool) {
(self % rhs, false)
}
#[inline(always)]
fn overflowing_shl(self, rhs: u32) -> ($t, bool) {
(self << (rhs & self::shift_max::$t),
(rhs > self::shift_max::$t))
}
#[inline(always)]
fn overflowing_shr(self, rhs: u32) -> ($t, bool) {
(self >> (rhs & self::shift_max::$t),
(rhs > self::shift_max::$t))
}
}
)*)
}
signed_overflowing_impl! { i8 i16 i32 i64 }
unsigned_overflowing_impl! { u8 u16 u32 u64 }
#[cfg(target_pointer_width = "64")]
impl OverflowingOps for usize {

View file

@ -482,8 +482,10 @@ pub trait Neg {
fn neg(self) -> Self::Output;
}
macro_rules! neg_impl {
($($t:ty)*) => ($(
macro_rules! neg_impl_core {
($id:ident => $body:expr, $($t:ty)*) => ($(
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(unsigned_negation)]
impl Neg for $t {
@ -492,14 +494,28 @@ impl Neg for $t {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
fn neg(self) -> $t { -self }
fn neg(self) -> $t { let $id = self; $body }
}
forward_ref_unop! { impl Neg, neg for $t }
)*)
}
neg_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
macro_rules! neg_impl_numeric {
($($t:ty)*) => { neg_impl_core!{ x => -x, $($t)*} }
}
macro_rules! neg_impl_unsigned {
($($t:ty)*) => {
neg_impl_core!{ x => {
#[cfg(stage0)]
use ::num::wrapping::WrappingOps;
!x.wrapping_add(1)
}, $($t)*} }
}
// neg_impl_unsigned! { usize u8 u16 u32 u64 }
neg_impl_numeric! { isize i8 i16 i32 i64 f32 f64 }
/// The `Not` trait is used to specify the functionality of unary `!`.
///

View file

@ -855,7 +855,7 @@ fn next(&mut self, haystack: &[u8], needle: &[u8], long_period: bool)
#[allow(dead_code)]
#[allow(deprecated)]
fn maximal_suffix(arr: &[u8], reversed: bool) -> (usize, usize) {
let mut left: usize = -1; // Corresponds to i in the paper
let mut left: usize = !0; // 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
let mut period = 1; // Corresponds to p in the paper

View file

@ -125,14 +125,14 @@ fn test_format_int_flags() {
assert!(format!("{:>8x}", 10) == " a");
assert!(format!("{:#08x}", 10) == "0x00000a");
assert!(format!("{:08}", -10) == "-0000010");
assert!(format!("{:x}", -1u8) == "ff");
assert!(format!("{:X}", -1u8) == "FF");
assert!(format!("{:b}", -1u8) == "11111111");
assert!(format!("{:o}", -1u8) == "377");
assert!(format!("{:#x}", -1u8) == "0xff");
assert!(format!("{:#X}", -1u8) == "0xFF");
assert!(format!("{:#b}", -1u8) == "0b11111111");
assert!(format!("{:#o}", -1u8) == "0o377");
assert!(format!("{:x}", !0u8) == "ff");
assert!(format!("{:X}", !0u8) == "FF");
assert!(format!("{:b}", !0u8) == "11111111");
assert!(format!("{:o}", !0u8) == "377");
assert!(format!("{:#x}", !0u8) == "0xff");
assert!(format!("{:#X}", !0u8) == "0xFF");
assert!(format!("{:#b}", !0u8) == "0b11111111");
assert!(format!("{:#o}", !0u8) == "0o377");
}
#[test]

View file

@ -2865,7 +2865,7 @@ pub mod posix88 {
pub const MAP_FIXED : c_int = 0x0010;
pub const MAP_ANON : c_int = 0x0020;
pub const MAP_FAILED : *mut c_void = -1 as *mut c_void;
pub const MAP_FAILED : *mut c_void = !0 as *mut c_void;
pub const MCL_CURRENT : c_int = 0x0001;
pub const MCL_FUTURE : c_int = 0x0002;
@ -4696,7 +4696,7 @@ pub mod posix88 {
pub const MAP_FIXED : c_int = 0x0010;
pub const MAP_ANON : c_int = 0x1000;
pub const MAP_FAILED : *mut c_void = -1 as *mut c_void;
pub const MAP_FAILED : *mut c_void = !0 as *mut c_void;
pub const MCL_CURRENT : c_int = 0x0001;
pub const MCL_FUTURE : c_int = 0x0002;

View file

@ -361,7 +361,7 @@ fn test_weighted_choice_zero_weight() {
}
#[test] #[should_panic]
fn test_weighted_choice_weight_overflows() {
let x = (-1) as usize / 2; // x + x + 2 is the overflow
let x = (!0) as usize / 2; // x + x + 2 is the overflow
WeightedChoice::new(&mut [Weighted { weight: x, item: 0 },
Weighted { weight: 1, item: 1 },
Weighted { weight: x, item: 2 },

View file

@ -23,6 +23,7 @@
use syntax::ast::{self, Expr};
use syntax::codemap::Span;
use syntax::feature_gate;
use syntax::parse::token::InternedString;
use syntax::ptr::P;
use syntax::{ast_map, ast_util, codemap};
@ -395,7 +396,7 @@ pub fn const_int_checked_neg<'a>(
pub fn const_uint_checked_neg<'a>(
a: u64, _e: &'a Expr, _opt_ety: Option<UintTy>) -> EvalResult {
// This always succeeds, and by definition, returns `(!a)+1`.
Ok(const_uint(-a))
Ok(const_uint((!a).wrapping_add(1)))
}
macro_rules! overflow_checking_body {
@ -594,7 +595,16 @@ fn fromb(b: bool) -> const_val { const_int(b as i64) }
match try!(eval_const_expr_partial(tcx, &**inner, ety)) {
const_float(f) => const_float(-f),
const_int(n) => try!(const_int_checked_neg(n, e, expr_int_type)),
const_uint(n) => try!(const_uint_checked_neg(n, e, expr_uint_type)),
const_uint(i) => {
if !tcx.sess.features.borrow().negate_unsigned {
feature_gate::emit_feature_err(
&tcx.sess.parse_sess.span_diagnostic,
"negate_unsigned",
e.span,
"unary negation of unsigned integers may be removed in the future");
}
try!(const_uint_checked_neg(i, e, expr_uint_type))
}
const_str(_) => signal!(e, NegateOnString),
const_bool(_) => signal!(e, NegateOnBoolean),
const_binary(_) => signal!(e, NegateOnBinary),

View file

@ -116,7 +116,7 @@ pub struct TypeLimits {
impl TypeLimits {
pub fn new() -> TypeLimits {
TypeLimits {
negated_expr_id: -1,
negated_expr_id: !0,
}
}
}

View file

@ -830,7 +830,7 @@ fn load_discr(bcx: Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr)
let bits = machine::llbitsize_of_real(bcx.ccx(), llty);
assert!(bits <= 64);
let bits = bits as usize;
let mask = (-1u64 >> (64 - bits)) as Disr;
let mask = (!0u64 >> (64 - bits)) as Disr;
// For a (max) discr of -1, max will be `-1 as usize`, which overflows.
// However, that is fine here (it would still represent the full range),
if (max.wrapping_add(1)) & mask == min & mask {

View file

@ -868,7 +868,7 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(
_ => unreachable!(),
};
let minus_one = ICmp(bcx, llvm::IntEQ, rhs,
C_integral(llty, -1, false), debug_loc);
C_integral(llty, !0, false), debug_loc);
with_cond(bcx, minus_one, |bcx| {
let is_min = ICmp(bcx, llvm::IntEQ, lhs,
C_integral(llty, min, true), debug_loc);
@ -1388,7 +1388,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
common::validate_substs(param_substs);
debug!("new_fn_ctxt(path={}, id={}, param_substs={})",
if id == -1 {
if id == !0 {
"".to_string()
} else {
ccx.tcx().map.path_to_string(id).to_string()

View file

@ -459,7 +459,7 @@ fn dummy_ccx<'a>(&'a self, shared: &'a SharedCrateContext<'tcx>)
CrateContext {
shared: shared,
local: self,
index: -1 as usize,
index: !0 as usize,
}
}
}

View file

@ -120,6 +120,7 @@
use syntax::ast::{self, DefId, Visibility};
use syntax::ast_util::{self, local_def};
use syntax::codemap::{self, Span};
use syntax::feature_gate;
use syntax::owned_slice::OwnedSlice;
use syntax::parse::token;
use syntax::print::pprust;
@ -3258,6 +3259,15 @@ fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
tcx.lang_items.neg_trait(),
expr, &**oprnd, oprnd_t, unop);
}
if let ty::ty_uint(_) = oprnd_t.sty {
if !tcx.sess.features.borrow().negate_unsigned {
feature_gate::emit_feature_err(
&tcx.sess.parse_sess.span_diagnostic,
"negate_unsigned",
expr.span,
"unary negation of unsigned integers may be removed in the future");
}
}
}
}
}

View file

@ -946,7 +946,7 @@ fn file_test_io_smoke_test() {
let mut read_stream = check!(File::open(filename));
let mut read_buf = [0; 1028];
let read_str = match check!(read_stream.read(&mut read_buf)) {
-1|0 => panic!("shouldn't happen"),
0 => panic!("shouldn't happen"),
n => str::from_utf8(&read_buf[..n]).unwrap().to_string()
};
assert_eq!(read_str, message);

View file

@ -970,7 +970,7 @@ fn file_test_io_smoke_test() {
let mut read_stream = File::open_mode(filename, Open, Read);
let mut read_buf = [0; 1028];
let read_str = match check!(read_stream.read(&mut read_buf)) {
-1|0 => panic!("shouldn't happen"),
0 => panic!("shouldn't happen"),
n => str::from_utf8(&read_buf[..n]).unwrap().to_string()
};
assert_eq!(read_str, message);

View file

@ -116,7 +116,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
use libc;
use libc::funcs::posix01::signal::signal;
unsafe {
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != -1);
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0);
}
}
ignore_sigpipe();

View file

@ -388,7 +388,7 @@ pub fn local_id(&self) -> NodeId {
/// When parsing and doing expansions, we initially give all AST nodes this AST
/// node value. Then later, in the renumber pass, we renumber them to have
/// small, positive ids.
pub const DUMMY_NODE_ID: NodeId = -1;
pub const DUMMY_NODE_ID: NodeId = !0;
/// The AST represents all type param bounds as types.
/// typeck::collect::compute_bounds matches these against

View file

@ -278,9 +278,9 @@ pub struct ExpnInfo {
#[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)]
pub struct ExpnId(u32);
pub const NO_EXPANSION: ExpnId = ExpnId(-1);
pub const NO_EXPANSION: ExpnId = ExpnId(!0);
// For code appearing from the command line
pub const COMMAND_LINE_EXPN: ExpnId = ExpnId(-2);
pub const COMMAND_LINE_EXPN: ExpnId = ExpnId(!1);
impl ExpnId {
pub fn from_llvm_cookie(cookie: c_uint) -> ExpnId {

View file

@ -152,6 +152,9 @@
// #23121. Array patterns have some hazards yet.
("slice_patterns", "1.0.0", Active),
// Allows use of unary negate on unsigned integers, e.g. -e for e: u8
("negate_unsigned", "1.0.0", Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)
@ -325,6 +328,7 @@ pub struct Features {
pub allow_custom_derive: bool,
pub simd_ffi: bool,
pub unmarked_api: bool,
pub negate_unsigned: bool,
/// spans of #![feature] attrs for stable language features. for error reporting
pub declared_stable_lang_features: Vec<Span>,
/// #![feature] attrs for non-language (library) features
@ -346,6 +350,7 @@ pub fn new() -> Features {
allow_custom_derive: false,
simd_ffi: false,
unmarked_api: false,
negate_unsigned: false,
declared_stable_lang_features: Vec::new(),
declared_lib_features: Vec::new()
}
@ -724,6 +729,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
allow_custom_derive: cx.has_feature("custom_derive"),
simd_ffi: cx.has_feature("simd_ffi"),
unmarked_api: cx.has_feature("unmarked_api"),
negate_unsigned: cx.has_feature("negate_unsigned"),
declared_stable_lang_features: accepted_features,
declared_lib_features: unknown_features
}

View file

@ -3053,7 +3053,7 @@ fn test_variant_to_string() {
#[test]
fn test_signed_int_to_string() {
let pos_int = ast::LitInt(42, ast::SignedIntLit(ast::TyI32, ast::Plus));
let neg_int = ast::LitInt((-42) as u64, ast::SignedIntLit(ast::TyI32, ast::Minus));
let neg_int = ast::LitInt((!42 + 1) as u64, ast::SignedIntLit(ast::TyI32, ast::Minus));
assert_eq!(format!("-{}", lit_to_string(&codemap::dummy_spanned(pos_int))),
lit_to_string(&codemap::dummy_spanned(neg_int)));
}

View file

@ -8,7 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(negate_unsigned)]
#![allow(unused_imports)]
#![feature(negate_unsigned)]
// Note: the relevant lint pass here runs before some of the constant
// evaluation below (e.g. that performed by trans and llvm), so if you

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(negate_unsigned)]
#[repr(u8)] //~ NOTE discriminant type specified here
enum Eu8 {
Au8 = 23,

View file

@ -8,10 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(negate_unsigned)]
#![deny(exceeding_bitshifts)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![feature(core)]
#![feature(core, negate_unsigned)]
fn main() {
let n = 1u8 << 7;

View file

@ -8,7 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(negate_unsigned)]
#![allow(dead_code)]
#![feature(negate_unsigned)]
// compile-flags: -D unused-comparisons
fn main() { }

View file

@ -16,10 +16,10 @@
#![deny(overflowing_literals)]
pub fn main() {
assert_eq!(0xffffffff, (-1 as u32));
assert_eq!(4294967295, (-1 as u32));
assert_eq!(0xffffffffffffffff, (-1 as u64));
assert_eq!(18446744073709551615, (-1 as u64));
assert_eq!(0xffffffff, (!0 as u32));
assert_eq!(4294967295, (!0 as u32));
assert_eq!(0xffffffffffffffff, (!0 as u64));
assert_eq!(18446744073709551615, (!0 as u64));
assert_eq!((-2147483648i32).wrapping_sub(1), 2147483647);
}

View file

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(negate_unsigned)]
#[cfg(any(target_arch = "x86", target_arch = "arm"))]
fn target() {

View file

@ -10,6 +10,7 @@
// pretty-expanded FIXME #23616
#![feature(negate_unsigned)]
#![feature(intrinsics)]
mod rusti {

View file

@ -10,10 +10,10 @@
// pretty-expanded FIXME #23616
static X2: u64 = -1 as u16 as u64;
static Y2: u64 = -1 as u32 as u64;
const X: u64 = -1 as u16 as u64;
const Y: u64 = -1 as u32 as u64;
static X2: u64 = !0 as u16 as u64;
static Y2: u64 = !0 as u32 as u64;
const X: u64 = !0 as u16 as u64;
const Y: u64 = !0 as u32 as u64;
fn main() {
assert_eq!(match 1 {