mirror of
https://github.com/rust-lang/rust
synced 2024-09-15 22:50:55 +00:00
rollup merge of #23863: pnkfelix/arith-oflo-const-eval
const_eval : add overflow-checking for {`+`, `-`, `*`, `/`, `<<`, `>>`}. One tricky detail here: There is some duplication of labor between `rustc::middle::const_eval` and `rustc_trans::trans::consts`. It might be good to explore ways to try to factor out the common structure to the two passes (by abstracting over the particular value-representation used in the compile-time interpreter). ---- Update: Rebased atop #23841 Fix #22531 Fix #23030 Fix #23221 Fix #23235
This commit is contained in:
commit
4f643d79fc
|
@ -30,6 +30,8 @@
|
|||
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};
|
||||
|
||||
#[unstable(feature = "core", reason = "may be removed, renamed, or relocated")]
|
||||
#[deprecated(since = "1.0.0", reason = "moved to inherent methods")]
|
||||
pub trait WrappingOps {
|
||||
|
@ -43,6 +45,12 @@ pub trait OverflowingOps {
|
|||
fn overflowing_add(self, rhs: Self) -> (Self, bool);
|
||||
fn overflowing_sub(self, rhs: Self) -> (Self, bool);
|
||||
fn overflowing_mul(self, rhs: Self) -> (Self, bool);
|
||||
|
||||
fn overflowing_div(self, rhs: Self) -> (Self, bool);
|
||||
fn overflowing_rem(self, rhs: Self) -> (Self, bool);
|
||||
|
||||
fn overflowing_shl(self, rhs: u32) -> (Self, bool);
|
||||
fn overflowing_shr(self, rhs: u32) -> (Self, bool);
|
||||
}
|
||||
|
||||
macro_rules! sh_impl {
|
||||
|
@ -184,6 +192,20 @@ fn bitand(self, other: Wrapping<$t>) -> Wrapping<$t> {
|
|||
|
||||
wrapping_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
|
||||
|
||||
mod shift_max {
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
pub const i8: u32 = (1 << 3) - 1;
|
||||
pub const i16: u32 = (1 << 4) - 1;
|
||||
pub const i32: u32 = (1 << 5) - 1;
|
||||
pub const i64: u32 = (1 << 6) - 1;
|
||||
|
||||
pub const u8: u32 = i8;
|
||||
pub const u16: u32 = i16;
|
||||
pub const u32: u32 = i32;
|
||||
pub const u64: u32 = i64;
|
||||
}
|
||||
|
||||
macro_rules! overflowing_impl {
|
||||
($($t:ident)*) => ($(
|
||||
impl OverflowingOps for $t {
|
||||
|
@ -205,6 +227,34 @@ fn overflowing_mul(self, rhs: $t) -> ($t, bool) {
|
|||
concat_idents!($t, _mul_with_overflow)(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn overflowing_div(self, rhs: $t) -> ($t, bool) {
|
||||
if self == $t::MIN && rhs == -1 {
|
||||
(1, true)
|
||||
} else {
|
||||
(self/rhs, false)
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_rem(self, rhs: $t) -> ($t, bool) {
|
||||
if self == $t::MIN && rhs == -1 {
|
||||
(0, true)
|
||||
} else {
|
||||
(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))
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
@ -234,6 +284,26 @@ fn overflowing_mul(self, rhs: usize) -> (usize, bool) {
|
|||
(res.0 as usize, res.1)
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_div(self, rhs: usize) -> (usize, bool) {
|
||||
let (r, f) = (self as u64).overflowing_div(rhs as u64);
|
||||
(r as usize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_rem(self, rhs: usize) -> (usize, bool) {
|
||||
let (r, f) = (self as u64).overflowing_rem(rhs as u64);
|
||||
(r as usize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_shl(self, rhs: u32) -> (usize, bool) {
|
||||
let (r, f) = (self as u64).overflowing_shl(rhs);
|
||||
(r as usize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_shr(self, rhs: u32) -> (usize, bool) {
|
||||
let (r, f) = (self as u64).overflowing_shr(rhs);
|
||||
(r as usize, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
|
@ -259,6 +329,26 @@ fn overflowing_mul(self, rhs: usize) -> (usize, bool) {
|
|||
(res.0 as usize, res.1)
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_div(self, rhs: usize) -> (usize, bool) {
|
||||
let (r, f) = (self as u32).overflowing_div(rhs as u32);
|
||||
(r as usize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_rem(self, rhs: usize) -> (usize, bool) {
|
||||
let (r, f) = (self as u32).overflowing_rem(rhs as u32);
|
||||
(r as usize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_shl(self, rhs: u32) -> (usize, bool) {
|
||||
let (r, f) = (self as u32).overflowing_shl(rhs);
|
||||
(r as usize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_shr(self, rhs: u32) -> (usize, bool) {
|
||||
let (r, f) = (self as u32).overflowing_shr(rhs);
|
||||
(r as usize, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
|
@ -284,6 +374,26 @@ fn overflowing_mul(self, rhs: isize) -> (isize, bool) {
|
|||
(res.0 as isize, res.1)
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_div(self, rhs: isize) -> (isize, bool) {
|
||||
let (r, f) = (self as i64).overflowing_div(rhs as i64);
|
||||
(r as isize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_rem(self, rhs: isize) -> (isize, bool) {
|
||||
let (r, f) = (self as i64).overflowing_rem(rhs as i64);
|
||||
(r as isize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_shl(self, rhs: u32) -> (isize, bool) {
|
||||
let (r, f) = (self as i64).overflowing_shl(rhs);
|
||||
(r as isize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_shr(self, rhs: u32) -> (isize, bool) {
|
||||
let (r, f) = (self as i64).overflowing_shr(rhs);
|
||||
(r as isize, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
|
@ -309,4 +419,24 @@ fn overflowing_mul(self, rhs: isize) -> (isize, bool) {
|
|||
(res.0 as isize, res.1)
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_div(self, rhs: isize) -> (isize, bool) {
|
||||
let (r, f) = (self as i32).overflowing_div(rhs as i32);
|
||||
(r as isize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_rem(self, rhs: isize) -> (isize, bool) {
|
||||
let (r, f) = (self as i32).overflowing_rem(rhs as i32);
|
||||
(r as isize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_shl(self, rhs: u32) -> (isize, bool) {
|
||||
let (r, f) = (self as i32).overflowing_shl(rhs);
|
||||
(r as isize, f)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn overflowing_shr(self, rhs: u32) -> (isize, bool) {
|
||||
let (r, f) = (self as i32).overflowing_shr(rhs);
|
||||
(r as isize, f)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ mod tests {
|
|||
fn test_overflows() {
|
||||
assert!(MAX > 0);
|
||||
assert!(MIN <= 0);
|
||||
assert!(MIN + MAX + 1 == 0);
|
||||
assert!((MIN + MAX).wrapping_add(1) == 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -68,6 +68,8 @@
|
|||
E0019,
|
||||
E0020,
|
||||
E0022,
|
||||
E0079, // enum variant: expected signed integer constant
|
||||
E0080, // enum variant: constant evaluation error
|
||||
E0109,
|
||||
E0110,
|
||||
E0133,
|
||||
|
@ -128,7 +130,8 @@
|
|||
E0313, // lifetime of borrowed pointer outlives lifetime of captured variable
|
||||
E0314, // closure outlives stack frame
|
||||
E0315, // cannot invoke closure outside of its lifetime
|
||||
E0316 // nested quantification of lifetimes
|
||||
E0316, // nested quantification of lifetimes
|
||||
E0370 // discriminant overflow
|
||||
}
|
||||
|
||||
__build_diagnostic_array! { DIAGNOSTICS }
|
||||
|
|
|
@ -1197,7 +1197,7 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
|
|||
})
|
||||
}
|
||||
|
||||
if let Some(ty) = tcx.node_types.borrow().get(&id) {
|
||||
if let Some(ty) = tcx.node_types().get(&id) {
|
||||
rbml_w.tag(c::tag_table_node_type, |rbml_w| {
|
||||
rbml_w.id(id);
|
||||
rbml_w.emit_ty(ecx, *ty);
|
||||
|
@ -1884,7 +1884,7 @@ fn decode_side_tables(dcx: &DecodeContext,
|
|||
let ty = val_dsr.read_ty(dcx);
|
||||
debug!("inserting ty for node {}: {}",
|
||||
id, ty_to_string(dcx.tcx, ty));
|
||||
dcx.tcx.node_types.borrow_mut().insert(id, ty);
|
||||
dcx.tcx.node_type_insert(id, ty);
|
||||
}
|
||||
c::tag_table_item_subst => {
|
||||
let item_substs = ty::ItemSubsts {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
pub use self::const_val::*;
|
||||
|
||||
use self::ErrKind::*;
|
||||
|
||||
use metadata::csearch;
|
||||
use middle::{astencode, def};
|
||||
use middle::pat_util::def_to_path;
|
||||
|
@ -27,6 +29,7 @@
|
|||
|
||||
use std::borrow::{Cow, IntoCow};
|
||||
use std::num::wrapping::OverflowingOps;
|
||||
use std::num::ToPrimitive;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::Entry::Vacant;
|
||||
use std::{i8, i16, i32, i64};
|
||||
|
@ -234,6 +237,7 @@ pub enum ErrKind {
|
|||
NotOnStruct,
|
||||
NotOnTuple,
|
||||
|
||||
NegateWithOverflow(i64),
|
||||
AddiWithOverflow(i64, i64),
|
||||
SubiWithOverflow(i64, i64),
|
||||
MuliWithOverflow(i64, i64),
|
||||
|
@ -244,6 +248,8 @@ pub enum ErrKind {
|
|||
DivideWithOverflow,
|
||||
ModuloByZero,
|
||||
ModuloWithOverflow,
|
||||
ShiftLeftWithOverflow,
|
||||
ShiftRightWithOverflow,
|
||||
MissingStructField,
|
||||
NonConstPath,
|
||||
ExpectedConstTuple,
|
||||
|
@ -257,6 +263,7 @@ pub enum ErrKind {
|
|||
impl ConstEvalErr {
|
||||
pub fn description(&self) -> Cow<str> {
|
||||
use self::ErrKind::*;
|
||||
|
||||
match self.kind {
|
||||
CannotCast => "can't cast this type".into_cow(),
|
||||
CannotCastTo(s) => format!("can't cast this type to {}", s).into_cow(),
|
||||
|
@ -275,6 +282,7 @@ pub fn description(&self) -> Cow<str> {
|
|||
NotOnStruct => "not on struct".into_cow(),
|
||||
NotOnTuple => "not on tuple".into_cow(),
|
||||
|
||||
NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(),
|
||||
AddiWithOverflow(..) => "attempted to add with overflow".into_cow(),
|
||||
SubiWithOverflow(..) => "attempted to sub with overflow".into_cow(),
|
||||
MuliWithOverflow(..) => "attempted to mul with overflow".into_cow(),
|
||||
|
@ -285,6 +293,8 @@ pub fn description(&self) -> Cow<str> {
|
|||
DivideWithOverflow => "attempted to divide with overflow".into_cow(),
|
||||
ModuloByZero => "attempted remainder with a divisor of zero".into_cow(),
|
||||
ModuloWithOverflow => "attempted remainder with overflow".into_cow(),
|
||||
ShiftLeftWithOverflow => "attempted left shift with overflow".into_cow(),
|
||||
ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(),
|
||||
MissingStructField => "nonexistent struct field".into_cow(),
|
||||
NonConstPath => "non-constant path in constant expr".into_cow(),
|
||||
ExpectedConstTuple => "expected constant tuple".into_cow(),
|
||||
|
@ -297,57 +307,294 @@ pub fn description(&self) -> Cow<str> {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! signal {
|
||||
($e:expr, $ctor:ident) => {
|
||||
return Err(ConstEvalErr { span: $e.span, kind: ErrKind::$ctor })
|
||||
};
|
||||
pub type EvalResult = Result<const_val, ConstEvalErr>;
|
||||
pub type CastResult = Result<const_val, ErrKind>;
|
||||
|
||||
($e:expr, $ctor:ident($($arg:expr),*)) => {
|
||||
return Err(ConstEvalErr { span: $e.span, kind: ErrKind::$ctor($($arg),*) })
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum IntTy { I8, I16, I32, I64 }
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum UintTy { U8, U16, U32, U64 }
|
||||
|
||||
impl IntTy {
|
||||
pub fn from(tcx: &ty::ctxt, t: ast::IntTy) -> IntTy {
|
||||
let t = if let ast::TyIs = t {
|
||||
tcx.sess.target.int_type
|
||||
} else {
|
||||
t
|
||||
};
|
||||
match t {
|
||||
ast::TyIs => unreachable!(),
|
||||
ast::TyI8 => IntTy::I8,
|
||||
ast::TyI16 => IntTy::I16,
|
||||
ast::TyI32 => IntTy::I32,
|
||||
ast::TyI64 => IntTy::I64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_add_int(e: &Expr, a: i64, b: i64) -> Result<const_val, ConstEvalErr> {
|
||||
let (ret, oflo) = a.overflowing_add(b);
|
||||
if !oflo { Ok(const_int(ret)) } else { signal!(e, AddiWithOverflow(a, b)) }
|
||||
}
|
||||
fn checked_sub_int(e: &Expr, a: i64, b: i64) -> Result<const_val, ConstEvalErr> {
|
||||
let (ret, oflo) = a.overflowing_sub(b);
|
||||
if !oflo { Ok(const_int(ret)) } else { signal!(e, SubiWithOverflow(a, b)) }
|
||||
}
|
||||
fn checked_mul_int(e: &Expr, a: i64, b: i64) -> Result<const_val, ConstEvalErr> {
|
||||
let (ret, oflo) = a.overflowing_mul(b);
|
||||
if !oflo { Ok(const_int(ret)) } else { signal!(e, MuliWithOverflow(a, b)) }
|
||||
impl UintTy {
|
||||
pub fn from(tcx: &ty::ctxt, t: ast::UintTy) -> UintTy {
|
||||
let t = if let ast::TyUs = t {
|
||||
tcx.sess.target.uint_type
|
||||
} else {
|
||||
t
|
||||
};
|
||||
match t {
|
||||
ast::TyUs => unreachable!(),
|
||||
ast::TyU8 => UintTy::U8,
|
||||
ast::TyU16 => UintTy::U16,
|
||||
ast::TyU32 => UintTy::U32,
|
||||
ast::TyU64 => UintTy::U64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_add_uint(e: &Expr, a: u64, b: u64) -> Result<const_val, ConstEvalErr> {
|
||||
let (ret, oflo) = a.overflowing_add(b);
|
||||
if !oflo { Ok(const_uint(ret)) } else { signal!(e, AdduWithOverflow(a, b)) }
|
||||
}
|
||||
fn checked_sub_uint(e: &Expr, a: u64, b: u64) -> Result<const_val, ConstEvalErr> {
|
||||
let (ret, oflo) = a.overflowing_sub(b);
|
||||
if !oflo { Ok(const_uint(ret)) } else { signal!(e, SubuWithOverflow(a, b)) }
|
||||
}
|
||||
fn checked_mul_uint(e: &Expr, a: u64, b: u64) -> Result<const_val, ConstEvalErr> {
|
||||
let (ret, oflo) = a.overflowing_mul(b);
|
||||
if !oflo { Ok(const_uint(ret)) } else { signal!(e, MuluWithOverflow(a, b)) }
|
||||
macro_rules! signal {
|
||||
($e:expr, $exn:expr) => {
|
||||
return Err(ConstEvalErr { span: $e.span, kind: $exn })
|
||||
}
|
||||
}
|
||||
|
||||
// The const_{int,uint}_checked_{neg,add,sub,mul,div,shl,shr} family
|
||||
// of functions catch and signal overflow errors during constant
|
||||
// evaluation.
|
||||
//
|
||||
// They all take the operator's arguments (`a` and `b` if binary), the
|
||||
// overall expression (`e`) and, if available, whole expression's
|
||||
// concrete type (`opt_ety`).
|
||||
//
|
||||
// If the whole expression's concrete type is None, then this is a
|
||||
// constant evaluation happening before type check (e.g. in the check
|
||||
// to confirm that a pattern range's left-side is not greater than its
|
||||
// right-side). We do not do arithmetic modulo the type's bitwidth in
|
||||
// such a case; we just do 64-bit arithmetic and assume that later
|
||||
// passes will do it again with the type information, and thus do the
|
||||
// overflow checks then.
|
||||
|
||||
pub fn const_int_checked_neg<'a>(
|
||||
a: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
|
||||
|
||||
let (min,max) = match opt_ety {
|
||||
// (-i8::MIN is itself not an i8, etc, but this is an easy way
|
||||
// to allow literals to pass the check. Of course that does
|
||||
// not work for i64::MIN.)
|
||||
Some(IntTy::I8) => (-(i8::MAX as i64), -(i8::MIN as i64)),
|
||||
Some(IntTy::I16) => (-(i16::MAX as i64), -(i16::MIN as i64)),
|
||||
Some(IntTy::I32) => (-(i32::MAX as i64), -(i32::MIN as i64)),
|
||||
None | Some(IntTy::I64) => (-i64::MAX, -(i64::MIN+1)),
|
||||
};
|
||||
|
||||
let oflo = a < min || a > max;
|
||||
if oflo {
|
||||
signal!(e, NegateWithOverflow(a));
|
||||
} else {
|
||||
Ok(const_int(-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))
|
||||
}
|
||||
|
||||
macro_rules! overflow_checking_body {
|
||||
($a:ident, $b:ident, $ety:ident, $overflowing_op:ident,
|
||||
lhs: $to_8_lhs:ident $to_16_lhs:ident $to_32_lhs:ident,
|
||||
rhs: $to_8_rhs:ident $to_16_rhs:ident $to_32_rhs:ident $to_64_rhs:ident,
|
||||
$EnumTy:ident $T8: ident $T16: ident $T32: ident $T64: ident,
|
||||
$result_type: ident) => { {
|
||||
let (a,b,opt_ety) = ($a,$b,$ety);
|
||||
match opt_ety {
|
||||
Some($EnumTy::$T8) => match (a.$to_8_lhs(), b.$to_8_rhs()) {
|
||||
(Some(a), Some(b)) => {
|
||||
let (a, oflo) = a.$overflowing_op(b);
|
||||
(a as $result_type, oflo)
|
||||
}
|
||||
(None, _) | (_, None) => (0, true)
|
||||
},
|
||||
Some($EnumTy::$T16) => match (a.$to_16_lhs(), b.$to_16_rhs()) {
|
||||
(Some(a), Some(b)) => {
|
||||
let (a, oflo) = a.$overflowing_op(b);
|
||||
(a as $result_type, oflo)
|
||||
}
|
||||
(None, _) | (_, None) => (0, true)
|
||||
},
|
||||
Some($EnumTy::$T32) => match (a.$to_32_lhs(), b.$to_32_rhs()) {
|
||||
(Some(a), Some(b)) => {
|
||||
let (a, oflo) = a.$overflowing_op(b);
|
||||
(a as $result_type, oflo)
|
||||
}
|
||||
(None, _) | (_, None) => (0, true)
|
||||
},
|
||||
None | Some($EnumTy::$T64) => match b.$to_64_rhs() {
|
||||
Some(b) => a.$overflowing_op(b),
|
||||
None => (0, true),
|
||||
}
|
||||
}
|
||||
} }
|
||||
}
|
||||
|
||||
macro_rules! int_arith_body {
|
||||
($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
|
||||
overflow_checking_body!(
|
||||
$a, $b, $ety, $overflowing_op,
|
||||
lhs: to_i8 to_i16 to_i32,
|
||||
rhs: to_i8 to_i16 to_i32 to_i64, IntTy I8 I16 I32 I64, i64)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! uint_arith_body {
|
||||
($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
|
||||
overflow_checking_body!(
|
||||
$a, $b, $ety, $overflowing_op,
|
||||
lhs: to_u8 to_u16 to_u32,
|
||||
rhs: to_u8 to_u16 to_u32 to_u64, UintTy U8 U16 U32 U64, u64)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! int_shift_body {
|
||||
($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
|
||||
overflow_checking_body!(
|
||||
$a, $b, $ety, $overflowing_op,
|
||||
lhs: to_i8 to_i16 to_i32,
|
||||
rhs: to_u32 to_u32 to_u32 to_u32, IntTy I8 I16 I32 I64, i64)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! uint_shift_body {
|
||||
($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
|
||||
overflow_checking_body!(
|
||||
$a, $b, $ety, $overflowing_op,
|
||||
lhs: to_u8 to_u16 to_u32,
|
||||
rhs: to_u32 to_u32 to_u32 to_u32, UintTy U8 U16 U32 U64, u64)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! pub_fn_checked_op {
|
||||
{$fn_name:ident ($a:ident : $a_ty:ty, $b:ident : $b_ty:ty,.. $WhichTy:ident) {
|
||||
$ret_oflo_body:ident $overflowing_op:ident
|
||||
$const_ty:ident $signal_exn:expr
|
||||
}} => {
|
||||
pub fn $fn_name<'a>($a: $a_ty,
|
||||
$b: $b_ty,
|
||||
e: &'a Expr,
|
||||
opt_ety: Option<$WhichTy>) -> EvalResult {
|
||||
let (ret, oflo) = $ret_oflo_body!($a, $b, opt_ety, $overflowing_op);
|
||||
if !oflo { Ok($const_ty(ret)) } else { signal!(e, $signal_exn) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub_fn_checked_op!{ const_int_checked_add(a: i64, b: i64,.. IntTy) {
|
||||
int_arith_body overflowing_add const_int AddiWithOverflow(a, b)
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_int_checked_sub(a: i64, b: i64,.. IntTy) {
|
||||
int_arith_body overflowing_sub const_int SubiWithOverflow(a, b)
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_int_checked_mul(a: i64, b: i64,.. IntTy) {
|
||||
int_arith_body overflowing_mul const_int MuliWithOverflow(a, b)
|
||||
}}
|
||||
|
||||
pub fn const_int_checked_div<'a>(
|
||||
a: i64, b: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
|
||||
if b == 0 { signal!(e, DivideByZero); }
|
||||
let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_div);
|
||||
if !oflo { Ok(const_int(ret)) } else { signal!(e, DivideWithOverflow) }
|
||||
}
|
||||
|
||||
pub fn const_int_checked_rem<'a>(
|
||||
a: i64, b: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
|
||||
if b == 0 { signal!(e, ModuloByZero); }
|
||||
let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_rem);
|
||||
if !oflo { Ok(const_int(ret)) } else { signal!(e, ModuloWithOverflow) }
|
||||
}
|
||||
|
||||
pub_fn_checked_op!{ const_int_checked_shl(a: i64, b: i64,.. IntTy) {
|
||||
int_shift_body overflowing_shl const_int ShiftLeftWithOverflow
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_int_checked_shl_via_uint(a: i64, b: u64,.. IntTy) {
|
||||
int_shift_body overflowing_shl const_int ShiftLeftWithOverflow
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_int_checked_shr(a: i64, b: i64,.. IntTy) {
|
||||
int_shift_body overflowing_shr const_int ShiftRightWithOverflow
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_int_checked_shr_via_uint(a: i64, b: u64,.. IntTy) {
|
||||
int_shift_body overflowing_shr const_int ShiftRightWithOverflow
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_uint_checked_add(a: u64, b: u64,.. UintTy) {
|
||||
uint_arith_body overflowing_add const_uint AdduWithOverflow(a, b)
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_uint_checked_sub(a: u64, b: u64,.. UintTy) {
|
||||
uint_arith_body overflowing_sub const_uint SubuWithOverflow(a, b)
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_uint_checked_mul(a: u64, b: u64,.. UintTy) {
|
||||
uint_arith_body overflowing_mul const_uint MuluWithOverflow(a, b)
|
||||
}}
|
||||
|
||||
pub fn const_uint_checked_div<'a>(
|
||||
a: u64, b: u64, e: &'a Expr, opt_ety: Option<UintTy>) -> EvalResult {
|
||||
if b == 0 { signal!(e, DivideByZero); }
|
||||
let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_div);
|
||||
if !oflo { Ok(const_uint(ret)) } else { signal!(e, DivideWithOverflow) }
|
||||
}
|
||||
|
||||
pub fn const_uint_checked_rem<'a>(
|
||||
a: u64, b: u64, e: &'a Expr, opt_ety: Option<UintTy>) -> EvalResult {
|
||||
if b == 0 { signal!(e, ModuloByZero); }
|
||||
let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_rem);
|
||||
if !oflo { Ok(const_uint(ret)) } else { signal!(e, ModuloWithOverflow) }
|
||||
}
|
||||
|
||||
pub_fn_checked_op!{ const_uint_checked_shl(a: u64, b: u64,.. UintTy) {
|
||||
uint_shift_body overflowing_shl const_uint ShiftLeftWithOverflow
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_uint_checked_shl_via_int(a: u64, b: i64,.. UintTy) {
|
||||
uint_shift_body overflowing_shl const_uint ShiftLeftWithOverflow
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_uint_checked_shr(a: u64, b: u64,.. UintTy) {
|
||||
uint_shift_body overflowing_shr const_uint ShiftRightWithOverflow
|
||||
}}
|
||||
|
||||
pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) {
|
||||
uint_shift_body overflowing_shr const_uint ShiftRightWithOverflow
|
||||
}}
|
||||
|
||||
pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
e: &Expr,
|
||||
ty_hint: Option<Ty<'tcx>>)
|
||||
-> Result<const_val, ConstEvalErr> {
|
||||
ty_hint: Option<Ty<'tcx>>) -> EvalResult {
|
||||
fn fromb(b: bool) -> const_val { const_int(b as i64) }
|
||||
|
||||
let ety = ty_hint.or_else(|| ty::expr_ty_opt(tcx, e));
|
||||
|
||||
// If type of expression itself is int or uint, normalize in these
|
||||
// bindings so that isize/usize is mapped to a type with an
|
||||
// inherently known bitwidth.
|
||||
let expr_int_type = ety.and_then(|ty| {
|
||||
if let ty::ty_int(t) = ty.sty {
|
||||
Some(IntTy::from(tcx, t)) } else { None }
|
||||
});
|
||||
let expr_uint_type = ety.and_then(|ty| {
|
||||
if let ty::ty_uint(t) = ty.sty {
|
||||
Some(UintTy::from(tcx, t)) } else { None }
|
||||
});
|
||||
|
||||
let result = match e.node {
|
||||
ast::ExprUnary(ast::UnNeg, ref inner) => {
|
||||
match try!(eval_const_expr_partial(tcx, &**inner, ety)) {
|
||||
const_float(f) => const_float(-f),
|
||||
const_int(i) => const_int(-i),
|
||||
const_uint(i) => const_uint(-i),
|
||||
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_str(_) => signal!(e, NegateOnString),
|
||||
const_bool(_) => signal!(e, NegateOnBoolean),
|
||||
const_binary(_) => signal!(e, NegateOnBinary),
|
||||
|
@ -391,51 +638,17 @@ fn fromb(b: bool) -> const_val { const_int(b as i64) }
|
|||
}
|
||||
}
|
||||
(const_int(a), const_int(b)) => {
|
||||
let is_a_min_value = || {
|
||||
let int_ty = match ty::expr_ty_opt(tcx, e).map(|ty| &ty.sty) {
|
||||
Some(&ty::ty_int(int_ty)) => int_ty,
|
||||
_ => return false
|
||||
};
|
||||
let int_ty = if let ast::TyIs = int_ty {
|
||||
tcx.sess.target.int_type
|
||||
} else {
|
||||
int_ty
|
||||
};
|
||||
match int_ty {
|
||||
ast::TyI8 => (a as i8) == i8::MIN,
|
||||
ast::TyI16 => (a as i16) == i16::MIN,
|
||||
ast::TyI32 => (a as i32) == i32::MIN,
|
||||
ast::TyI64 => (a as i64) == i64::MIN,
|
||||
ast::TyIs => unreachable!()
|
||||
}
|
||||
};
|
||||
match op.node {
|
||||
ast::BiAdd => try!(checked_add_int(e, a, b)),
|
||||
ast::BiSub => try!(checked_sub_int(e, a, b)),
|
||||
ast::BiMul => try!(checked_mul_int(e, a, b)),
|
||||
ast::BiDiv => {
|
||||
if b == 0 {
|
||||
signal!(e, DivideByZero);
|
||||
} else if b == -1 && is_a_min_value() {
|
||||
signal!(e, DivideWithOverflow);
|
||||
} else {
|
||||
const_int(a / b)
|
||||
}
|
||||
}
|
||||
ast::BiRem => {
|
||||
if b == 0 {
|
||||
signal!(e, ModuloByZero)
|
||||
} else if b == -1 && is_a_min_value() {
|
||||
signal!(e, ModuloWithOverflow)
|
||||
} else {
|
||||
const_int(a % b)
|
||||
}
|
||||
}
|
||||
ast::BiAdd => try!(const_int_checked_add(a,b,e,expr_int_type)),
|
||||
ast::BiSub => try!(const_int_checked_sub(a,b,e,expr_int_type)),
|
||||
ast::BiMul => try!(const_int_checked_mul(a,b,e,expr_int_type)),
|
||||
ast::BiDiv => try!(const_int_checked_div(a,b,e,expr_int_type)),
|
||||
ast::BiRem => try!(const_int_checked_rem(a,b,e,expr_int_type)),
|
||||
ast::BiAnd | ast::BiBitAnd => const_int(a & b),
|
||||
ast::BiOr | ast::BiBitOr => const_int(a | b),
|
||||
ast::BiBitXor => const_int(a ^ b),
|
||||
ast::BiShl => const_int(a << b as usize),
|
||||
ast::BiShr => const_int(a >> b as usize),
|
||||
ast::BiShl => try!(const_int_checked_shl(a,b,e,expr_int_type)),
|
||||
ast::BiShr => try!(const_int_checked_shr(a,b,e,expr_int_type)),
|
||||
ast::BiEq => fromb(a == b),
|
||||
ast::BiLt => fromb(a < b),
|
||||
ast::BiLe => fromb(a <= b),
|
||||
|
@ -446,18 +659,16 @@ fn fromb(b: bool) -> const_val { const_int(b as i64) }
|
|||
}
|
||||
(const_uint(a), const_uint(b)) => {
|
||||
match op.node {
|
||||
ast::BiAdd => try!(checked_add_uint(e, a, b)),
|
||||
ast::BiSub => try!(checked_sub_uint(e, a, b)),
|
||||
ast::BiMul => try!(checked_mul_uint(e, a, b)),
|
||||
ast::BiDiv if b == 0 => signal!(e, DivideByZero),
|
||||
ast::BiDiv => const_uint(a / b),
|
||||
ast::BiRem if b == 0 => signal!(e, ModuloByZero),
|
||||
ast::BiRem => const_uint(a % b),
|
||||
ast::BiAdd => try!(const_uint_checked_add(a,b,e,expr_uint_type)),
|
||||
ast::BiSub => try!(const_uint_checked_sub(a,b,e,expr_uint_type)),
|
||||
ast::BiMul => try!(const_uint_checked_mul(a,b,e,expr_uint_type)),
|
||||
ast::BiDiv => try!(const_uint_checked_div(a,b,e,expr_uint_type)),
|
||||
ast::BiRem => try!(const_uint_checked_rem(a,b,e,expr_uint_type)),
|
||||
ast::BiAnd | ast::BiBitAnd => const_uint(a & b),
|
||||
ast::BiOr | ast::BiBitOr => const_uint(a | b),
|
||||
ast::BiBitXor => const_uint(a ^ b),
|
||||
ast::BiShl => const_uint(a << b as usize),
|
||||
ast::BiShr => const_uint(a >> b as usize),
|
||||
ast::BiShl => try!(const_uint_checked_shl(a,b,e,expr_uint_type)),
|
||||
ast::BiShr => try!(const_uint_checked_shr(a,b,e,expr_uint_type)),
|
||||
ast::BiEq => fromb(a == b),
|
||||
ast::BiLt => fromb(a < b),
|
||||
ast::BiLe => fromb(a <= b),
|
||||
|
@ -469,15 +680,15 @@ fn fromb(b: bool) -> const_val { const_int(b as i64) }
|
|||
// shifts can have any integral type as their rhs
|
||||
(const_int(a), const_uint(b)) => {
|
||||
match op.node {
|
||||
ast::BiShl => const_int(a << b as usize),
|
||||
ast::BiShr => const_int(a >> b as usize),
|
||||
ast::BiShl => try!(const_int_checked_shl_via_uint(a,b,e,expr_int_type)),
|
||||
ast::BiShr => try!(const_int_checked_shr_via_uint(a,b,e,expr_int_type)),
|
||||
_ => signal!(e, InvalidOpForIntUint(op.node)),
|
||||
}
|
||||
}
|
||||
(const_uint(a), const_int(b)) => {
|
||||
match op.node {
|
||||
ast::BiShl => const_uint(a << b as usize),
|
||||
ast::BiShr => const_uint(a >> b as usize),
|
||||
ast::BiShl => try!(const_uint_checked_shl_via_int(a,b,e,expr_uint_type)),
|
||||
ast::BiShr => try!(const_uint_checked_shr_via_int(a,b,e,expr_uint_type)),
|
||||
_ => signal!(e, InvalidOpForUintInt(op.node)),
|
||||
}
|
||||
}
|
||||
|
@ -506,10 +717,15 @@ fn fromb(b: bool) -> const_val { const_int(b as i64) }
|
|||
tcx.sess.span_fatal(target_ty.span,
|
||||
"target type not found for const cast")
|
||||
});
|
||||
|
||||
// Prefer known type to noop, but always have a type hint.
|
||||
//
|
||||
// FIXME (#23833): the type-hint can cause problems,
|
||||
// e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
|
||||
// type to the sum, and thus no overflow is signaled.
|
||||
let base_hint = ty::expr_ty_opt(tcx, &**base).unwrap_or(ety);
|
||||
let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint)));
|
||||
match cast_const(val, ety) {
|
||||
match cast_const(tcx, val, ety) {
|
||||
Ok(val) => val,
|
||||
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
|
||||
}
|
||||
|
@ -607,39 +823,49 @@ fn fromb(b: bool) -> const_val { const_int(b as i64) }
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
fn cast_const(val: const_val, ty: Ty) -> Result<const_val, ErrKind> {
|
||||
macro_rules! define_casts {
|
||||
($($ty_pat:pat => (
|
||||
$intermediate_ty:ty,
|
||||
$const_type:ident,
|
||||
$target_ty:ty
|
||||
)),*) => (match ty.sty {
|
||||
$($ty_pat => {
|
||||
match val {
|
||||
const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
|
||||
const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
|
||||
const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
|
||||
const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
|
||||
_ => Err(ErrKind::CannotCastTo(stringify!($const_type))),
|
||||
}
|
||||
},)*
|
||||
_ => Err(ErrKind::CannotCast),
|
||||
})
|
||||
fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: const_val, ty: Ty) -> CastResult {
|
||||
macro_rules! convert_val {
|
||||
($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => {
|
||||
match val {
|
||||
const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
|
||||
const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
|
||||
const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
|
||||
const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
|
||||
_ => Err(ErrKind::CannotCastTo(stringify!($const_type))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_casts!{
|
||||
ty::ty_int(ast::TyIs) => (isize, const_int, i64),
|
||||
ty::ty_int(ast::TyI8) => (i8, const_int, i64),
|
||||
ty::ty_int(ast::TyI16) => (i16, const_int, i64),
|
||||
ty::ty_int(ast::TyI32) => (i32, const_int, i64),
|
||||
ty::ty_int(ast::TyI64) => (i64, const_int, i64),
|
||||
ty::ty_uint(ast::TyUs) => (usize, const_uint, u64),
|
||||
ty::ty_uint(ast::TyU8) => (u8, const_uint, u64),
|
||||
ty::ty_uint(ast::TyU16) => (u16, const_uint, u64),
|
||||
ty::ty_uint(ast::TyU32) => (u32, const_uint, u64),
|
||||
ty::ty_uint(ast::TyU64) => (u64, const_uint, u64),
|
||||
ty::ty_float(ast::TyF32) => (f32, const_float, f64),
|
||||
ty::ty_float(ast::TyF64) => (f64, const_float, f64)
|
||||
// Issue #23890: If isize/usize, then dispatch to appropriate target representation type
|
||||
match (&ty.sty, tcx.sess.target.int_type, tcx.sess.target.uint_type) {
|
||||
(&ty::ty_int(ast::TyIs), ast::TyI32, _) => return convert_val!(i32, const_int, i64),
|
||||
(&ty::ty_int(ast::TyIs), ast::TyI64, _) => return convert_val!(i64, const_int, i64),
|
||||
(&ty::ty_int(ast::TyIs), _, _) => panic!("unexpected target.int_type"),
|
||||
|
||||
(&ty::ty_uint(ast::TyUs), _, ast::TyU32) => return convert_val!(u32, const_uint, u64),
|
||||
(&ty::ty_uint(ast::TyUs), _, ast::TyU64) => return convert_val!(u64, const_uint, u64),
|
||||
(&ty::ty_uint(ast::TyUs), _, _) => panic!("unexpected target.uint_type"),
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match ty.sty {
|
||||
ty::ty_int(ast::TyIs) => unreachable!(),
|
||||
ty::ty_uint(ast::TyUs) => unreachable!(),
|
||||
|
||||
ty::ty_int(ast::TyI8) => convert_val!(i8, const_int, i64),
|
||||
ty::ty_int(ast::TyI16) => convert_val!(i16, const_int, i64),
|
||||
ty::ty_int(ast::TyI32) => convert_val!(i32, const_int, i64),
|
||||
ty::ty_int(ast::TyI64) => convert_val!(i64, const_int, i64),
|
||||
|
||||
ty::ty_uint(ast::TyU8) => convert_val!(u8, const_uint, u64),
|
||||
ty::ty_uint(ast::TyU16) => convert_val!(u16, const_uint, u64),
|
||||
ty::ty_uint(ast::TyU32) => convert_val!(u32, const_uint, u64),
|
||||
ty::ty_uint(ast::TyU64) => convert_val!(u64, const_uint, u64),
|
||||
|
||||
ty::ty_float(ast::TyF32) => convert_val!(f32, const_float, f64),
|
||||
ty::ty_float(ast::TyF64) => convert_val!(f64, const_float, f64),
|
||||
_ => Err(ErrKind::CannotCast),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,11 +68,13 @@
|
|||
|
||||
use arena::TypedArena;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::{Cell, RefCell, Ref};
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, SipHasher, Hasher};
|
||||
use std::mem;
|
||||
use std::num::ToPrimitive;
|
||||
use std::num::wrapping::WrappingOps;
|
||||
use std::ops;
|
||||
use std::rc::Rc;
|
||||
use std::vec::IntoIter;
|
||||
|
@ -83,9 +85,11 @@
|
|||
use syntax::ast::{MutImmutable, MutMutable, Name, NamedField, NodeId};
|
||||
use syntax::ast::{StmtExpr, StmtSemi, StructField, UnnamedField, Visibility};
|
||||
use syntax::ast_util::{self, is_local, lit_is_str, local_def};
|
||||
use syntax::attr::{self, AttrMetaMethods};
|
||||
use syntax::attr::{self, AttrMetaMethods, SignedInt, UnsignedInt};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::parse::token::{self, InternedString, special_idents};
|
||||
use syntax::print::pprust;
|
||||
use syntax::ptr::P;
|
||||
use syntax::{ast, ast_map};
|
||||
|
||||
pub type Disr = u64;
|
||||
|
@ -685,7 +689,7 @@ pub struct ctxt<'tcx> {
|
|||
/// Stores the types for various nodes in the AST. Note that this table
|
||||
/// is not guaranteed to be populated until after typeck. See
|
||||
/// typeck::check::fn_ctxt for details.
|
||||
pub node_types: RefCell<NodeMap<Ty<'tcx>>>,
|
||||
node_types: RefCell<NodeMap<Ty<'tcx>>>,
|
||||
|
||||
/// Stores the type parameters which were substituted to obtain the type
|
||||
/// of this node. This only applies to nodes that refer to entities
|
||||
|
@ -850,6 +854,13 @@ pub struct ctxt<'tcx> {
|
|||
pub const_qualif_map: RefCell<NodeMap<check_const::ConstQualif>>,
|
||||
}
|
||||
|
||||
impl<'tcx> ctxt<'tcx> {
|
||||
pub fn node_types(&self) -> Ref<NodeMap<Ty<'tcx>>> { self.node_types.borrow() }
|
||||
pub fn node_type_insert(&self, id: NodeId, ty: Ty<'tcx>) {
|
||||
self.node_types.borrow_mut().insert(id, ty);
|
||||
}
|
||||
}
|
||||
|
||||
// Flags that we track on types. These flags are propagated upwards
|
||||
// through the type during type construction, so that we can quickly
|
||||
// check whether the type has various kinds of types in it without
|
||||
|
@ -5489,63 +5500,268 @@ pub fn type_is_empty(cx: &ctxt, ty: Ty) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
trait IntTypeExt {
|
||||
fn to_ty<'tcx>(&self, cx: &ctxt<'tcx>) -> Ty<'tcx>;
|
||||
fn i64_to_disr(&self, val: i64) -> Option<Disr>;
|
||||
fn u64_to_disr(&self, val: u64) -> Option<Disr>;
|
||||
fn disr_incr(&self, val: Disr) -> Option<Disr>;
|
||||
fn disr_string(&self, val: Disr) -> String;
|
||||
fn disr_wrap_incr(&self, val: Option<Disr>) -> Disr;
|
||||
}
|
||||
|
||||
impl IntTypeExt for attr::IntType {
|
||||
fn to_ty<'tcx>(&self, cx: &ctxt<'tcx>) -> Ty<'tcx> {
|
||||
match *self {
|
||||
SignedInt(ast::TyI8) => cx.types.i8,
|
||||
SignedInt(ast::TyI16) => cx.types.i16,
|
||||
SignedInt(ast::TyI32) => cx.types.i32,
|
||||
SignedInt(ast::TyI64) => cx.types.i64,
|
||||
SignedInt(ast::TyIs) => cx.types.isize,
|
||||
UnsignedInt(ast::TyU8) => cx.types.u8,
|
||||
UnsignedInt(ast::TyU16) => cx.types.u16,
|
||||
UnsignedInt(ast::TyU32) => cx.types.u32,
|
||||
UnsignedInt(ast::TyU64) => cx.types.u64,
|
||||
UnsignedInt(ast::TyUs) => cx.types.usize,
|
||||
}
|
||||
}
|
||||
|
||||
fn i64_to_disr(&self, val: i64) -> Option<Disr> {
|
||||
match *self {
|
||||
SignedInt(ast::TyI8) => val.to_i8() .map(|v| v as Disr),
|
||||
SignedInt(ast::TyI16) => val.to_i16() .map(|v| v as Disr),
|
||||
SignedInt(ast::TyI32) => val.to_i32() .map(|v| v as Disr),
|
||||
SignedInt(ast::TyI64) => val.to_i64() .map(|v| v as Disr),
|
||||
UnsignedInt(ast::TyU8) => val.to_u8() .map(|v| v as Disr),
|
||||
UnsignedInt(ast::TyU16) => val.to_u16() .map(|v| v as Disr),
|
||||
UnsignedInt(ast::TyU32) => val.to_u32() .map(|v| v as Disr),
|
||||
UnsignedInt(ast::TyU64) => val.to_u64() .map(|v| v as Disr),
|
||||
|
||||
UnsignedInt(ast::TyUs) |
|
||||
SignedInt(ast::TyIs) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn u64_to_disr(&self, val: u64) -> Option<Disr> {
|
||||
match *self {
|
||||
SignedInt(ast::TyI8) => val.to_i8() .map(|v| v as Disr),
|
||||
SignedInt(ast::TyI16) => val.to_i16() .map(|v| v as Disr),
|
||||
SignedInt(ast::TyI32) => val.to_i32() .map(|v| v as Disr),
|
||||
SignedInt(ast::TyI64) => val.to_i64() .map(|v| v as Disr),
|
||||
UnsignedInt(ast::TyU8) => val.to_u8() .map(|v| v as Disr),
|
||||
UnsignedInt(ast::TyU16) => val.to_u16() .map(|v| v as Disr),
|
||||
UnsignedInt(ast::TyU32) => val.to_u32() .map(|v| v as Disr),
|
||||
UnsignedInt(ast::TyU64) => val.to_u64() .map(|v| v as Disr),
|
||||
|
||||
UnsignedInt(ast::TyUs) |
|
||||
SignedInt(ast::TyIs) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn disr_incr(&self, val: Disr) -> Option<Disr> {
|
||||
macro_rules! add1 {
|
||||
($e:expr) => { $e.and_then(|v|v.checked_add(1)).map(|v| v as Disr) }
|
||||
}
|
||||
match *self {
|
||||
// SignedInt repr means we *want* to reinterpret the bits
|
||||
// treating the highest bit of Disr as a sign-bit, so
|
||||
// cast to i64 before range-checking.
|
||||
SignedInt(ast::TyI8) => add1!((val as i64).to_i8()),
|
||||
SignedInt(ast::TyI16) => add1!((val as i64).to_i16()),
|
||||
SignedInt(ast::TyI32) => add1!((val as i64).to_i32()),
|
||||
SignedInt(ast::TyI64) => add1!(Some(val as i64)),
|
||||
|
||||
UnsignedInt(ast::TyU8) => add1!(val.to_u8()),
|
||||
UnsignedInt(ast::TyU16) => add1!(val.to_u16()),
|
||||
UnsignedInt(ast::TyU32) => add1!(val.to_u32()),
|
||||
UnsignedInt(ast::TyU64) => add1!(Some(val)),
|
||||
|
||||
UnsignedInt(ast::TyUs) |
|
||||
SignedInt(ast::TyIs) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// This returns a String because (1.) it is only used for
|
||||
// rendering an error message and (2.) a string can represent the
|
||||
// full range from `i64::MIN` through `u64::MAX`.
|
||||
fn disr_string(&self, val: Disr) -> String {
|
||||
match *self {
|
||||
SignedInt(ast::TyI8) => format!("{}", val as i8 ),
|
||||
SignedInt(ast::TyI16) => format!("{}", val as i16),
|
||||
SignedInt(ast::TyI32) => format!("{}", val as i32),
|
||||
SignedInt(ast::TyI64) => format!("{}", val as i64),
|
||||
UnsignedInt(ast::TyU8) => format!("{}", val as u8 ),
|
||||
UnsignedInt(ast::TyU16) => format!("{}", val as u16),
|
||||
UnsignedInt(ast::TyU32) => format!("{}", val as u32),
|
||||
UnsignedInt(ast::TyU64) => format!("{}", val as u64),
|
||||
|
||||
UnsignedInt(ast::TyUs) |
|
||||
SignedInt(ast::TyIs) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn disr_wrap_incr(&self, val: Option<Disr>) -> Disr {
|
||||
macro_rules! add1 {
|
||||
($e:expr) => { ($e).wrapping_add(1) as Disr }
|
||||
}
|
||||
let val = val.unwrap_or(ty::INITIAL_DISCRIMINANT_VALUE);
|
||||
match *self {
|
||||
SignedInt(ast::TyI8) => add1!(val as i8 ),
|
||||
SignedInt(ast::TyI16) => add1!(val as i16),
|
||||
SignedInt(ast::TyI32) => add1!(val as i32),
|
||||
SignedInt(ast::TyI64) => add1!(val as i64),
|
||||
UnsignedInt(ast::TyU8) => add1!(val as u8 ),
|
||||
UnsignedInt(ast::TyU16) => add1!(val as u16),
|
||||
UnsignedInt(ast::TyU32) => add1!(val as u32),
|
||||
UnsignedInt(ast::TyU64) => add1!(val as u64),
|
||||
|
||||
UnsignedInt(ast::TyUs) |
|
||||
SignedInt(ast::TyIs) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `(normalized_type, ty)`, where `normalized_type` is the
|
||||
/// IntType representation of one of {i64,i32,i16,i8,u64,u32,u16,u8},
|
||||
/// and `ty` is the original type (i.e. may include `isize` or
|
||||
/// `usize`).
|
||||
pub fn enum_repr_type<'tcx>(cx: &ctxt<'tcx>,
|
||||
opt_hint: Option<&attr::ReprAttr>)
|
||||
-> (attr::IntType, Ty<'tcx>)
|
||||
{
|
||||
let repr_type = match opt_hint {
|
||||
// Feed in the given type
|
||||
Some(&attr::ReprInt(_, int_t)) => int_t,
|
||||
// ... but provide sensible default if none provided
|
||||
//
|
||||
// NB. Historically `fn enum_variants` generate i64 here, while
|
||||
// rustc_typeck::check would generate isize.
|
||||
_ => SignedInt(ast::TyIs),
|
||||
};
|
||||
|
||||
let repr_type_ty = repr_type.to_ty(cx);
|
||||
let repr_type = match repr_type {
|
||||
SignedInt(ast::TyIs) =>
|
||||
SignedInt(cx.sess.target.int_type),
|
||||
UnsignedInt(ast::TyUs) =>
|
||||
UnsignedInt(cx.sess.target.uint_type),
|
||||
other => other
|
||||
};
|
||||
|
||||
(repr_type, repr_type_ty)
|
||||
}
|
||||
|
||||
fn report_discrim_overflow(cx: &ctxt,
|
||||
variant_span: Span,
|
||||
variant_name: &str,
|
||||
repr_type: attr::IntType,
|
||||
prev_val: Disr) {
|
||||
let computed_value = repr_type.disr_wrap_incr(Some(prev_val));
|
||||
let computed_value = repr_type.disr_string(computed_value);
|
||||
let prev_val = repr_type.disr_string(prev_val);
|
||||
let repr_type = repr_type.to_ty(cx).user_string(cx);
|
||||
span_err!(cx.sess, variant_span, E0370,
|
||||
"enum discriminant overflowed on value after {}: {}; \
|
||||
set explicitly via {} = {} if that is desired outcome",
|
||||
prev_val, repr_type, variant_name, computed_value);
|
||||
}
|
||||
|
||||
// This computes the discriminant values for the sequence of Variants
|
||||
// attached to a particular enum, taking into account the #[repr] (if
|
||||
// any) provided via the `opt_hint`.
|
||||
fn compute_enum_variants<'tcx>(cx: &ctxt<'tcx>,
|
||||
vs: &'tcx [P<ast::Variant>],
|
||||
opt_hint: Option<&attr::ReprAttr>)
|
||||
-> Vec<Rc<ty::VariantInfo<'tcx>>> {
|
||||
let mut variants: Vec<Rc<ty::VariantInfo>> = Vec::new();
|
||||
let mut prev_disr_val: Option<ty::Disr> = None;
|
||||
|
||||
let (repr_type, repr_type_ty) = ty::enum_repr_type(cx, opt_hint);
|
||||
|
||||
for v in vs {
|
||||
// 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 current_disr_val;
|
||||
|
||||
// This closure marks cases where, when an error occurs during
|
||||
// the computation, attempt to assign a (hopefully) fresh
|
||||
// value to avoid spurious error reports downstream.
|
||||
let attempt_fresh_value = move || -> Disr {
|
||||
repr_type.disr_wrap_incr(prev_disr_val)
|
||||
};
|
||||
|
||||
match v.node.disr_expr {
|
||||
Some(ref e) => {
|
||||
debug!("disr expr, checking {}", pprust::expr_to_string(&**e));
|
||||
|
||||
// check_expr (from check_const pass) doesn't guarantee
|
||||
// that the expression is in a form that eval_const_expr can
|
||||
// handle, so we may still get an internal compiler error
|
||||
//
|
||||
// pnkfelix: The above comment was transcribed from
|
||||
// the version of this code taken from rustc_typeck.
|
||||
// Presumably the implication is that we need to deal
|
||||
// with such ICE's as they arise.
|
||||
//
|
||||
// Since this can be called from `ty::enum_variants`
|
||||
// anyway, best thing is to make `eval_const_expr`
|
||||
// more robust (on case-by-case basis).
|
||||
|
||||
match const_eval::eval_const_expr_partial(cx, &**e, Some(repr_type_ty)) {
|
||||
Ok(const_eval::const_int(val)) => current_disr_val = val as Disr,
|
||||
Ok(const_eval::const_uint(val)) => current_disr_val = val as Disr,
|
||||
Ok(_) => {
|
||||
span_err!(cx.sess, e.span, E0079,
|
||||
"expected signed integer constant");
|
||||
current_disr_val = attempt_fresh_value();
|
||||
}
|
||||
Err(ref err) => {
|
||||
span_err!(cx.sess, err.span, E0080,
|
||||
"constant evaluation error: {}",
|
||||
err.description());
|
||||
current_disr_val = attempt_fresh_value();
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
current_disr_val = match prev_disr_val {
|
||||
Some(prev_disr_val) => {
|
||||
if let Some(v) = repr_type.disr_incr(prev_disr_val) {
|
||||
v
|
||||
} else {
|
||||
report_discrim_overflow(cx, v.span, v.node.name.as_str(),
|
||||
repr_type, prev_disr_val);
|
||||
attempt_fresh_value()
|
||||
}
|
||||
}
|
||||
None => ty::INITIAL_DISCRIMINANT_VALUE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let variant_info = Rc::new(VariantInfo::from_ast_variant(cx, &**v, current_disr_val));
|
||||
prev_disr_val = Some(current_disr_val);
|
||||
|
||||
variants.push(variant_info);
|
||||
}
|
||||
|
||||
return variants;
|
||||
}
|
||||
|
||||
pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
|
||||
-> Rc<Vec<Rc<VariantInfo<'tcx>>>> {
|
||||
memoized(&cx.enum_var_cache, id, |id: ast::DefId| {
|
||||
if ast::LOCAL_CRATE != id.krate {
|
||||
Rc::new(csearch::get_enum_variants(cx, id))
|
||||
} else {
|
||||
/*
|
||||
Although both this code and check_enum_variants in typeck/check
|
||||
call eval_const_expr, it should never get called twice for the same
|
||||
expr, since check_enum_variants also updates the enum_var_cache
|
||||
*/
|
||||
match cx.map.get(id.node) {
|
||||
ast_map::NodeItem(ref item) => {
|
||||
match item.node {
|
||||
ast::ItemEnum(ref enum_definition, _) => {
|
||||
let mut last_discriminant: Option<Disr> = None;
|
||||
Rc::new(enum_definition.variants.iter().map(|variant| {
|
||||
|
||||
let mut discriminant = INITIAL_DISCRIMINANT_VALUE;
|
||||
if let Some(ref e) = variant.node.disr_expr {
|
||||
// Preserve all values, and prefer signed.
|
||||
let ty = Some(cx.types.i64);
|
||||
match const_eval::eval_const_expr_partial(cx, &**e, ty) {
|
||||
Ok(const_eval::const_int(val)) => {
|
||||
discriminant = val as Disr;
|
||||
}
|
||||
Ok(const_eval::const_uint(val)) => {
|
||||
discriminant = val as Disr;
|
||||
}
|
||||
Ok(_) => {
|
||||
span_err!(cx.sess, e.span, E0304,
|
||||
"expected signed integer constant");
|
||||
}
|
||||
Err(err) => {
|
||||
span_err!(cx.sess, err.span, E0305,
|
||||
"constant evaluation error: {}",
|
||||
err.description());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
Rc::new(VariantInfo::from_ast_variant(cx, &**variant,
|
||||
discriminant))
|
||||
}).collect())
|
||||
Rc::new(compute_enum_variants(
|
||||
cx,
|
||||
&enum_definition.variants,
|
||||
lookup_repr_hints(cx, id).get(0)))
|
||||
}
|
||||
_ => {
|
||||
cx.sess.bug("enum_variants: id not bound to an enum")
|
||||
|
@ -5831,19 +6047,20 @@ pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> usize {
|
|||
"expected positive integer for repeat count, found {}",
|
||||
found);
|
||||
}
|
||||
Err(_) => {
|
||||
Err(err) => {
|
||||
let err_description = err.description();
|
||||
let found = match count_expr.node {
|
||||
ast::ExprPath(None, ast::Path {
|
||||
global: false,
|
||||
ref segments,
|
||||
..
|
||||
}) if segments.len() == 1 =>
|
||||
"variable",
|
||||
format!("{}", "found variable"),
|
||||
_ =>
|
||||
"non-constant expression"
|
||||
format!("but {}", err_description),
|
||||
};
|
||||
span_err!(tcx.sess, count_expr.span, E0307,
|
||||
"expected constant integer for repeat count, found {}",
|
||||
"expected constant integer for repeat count, {}",
|
||||
found);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1975,6 +1975,7 @@ pub fn LLVMDICompositeTypeSetTypeArray(Builder: DIBuilderRef,
|
|||
pub fn LLVMIsAArgument(value_ref: ValueRef) -> ValueRef;
|
||||
|
||||
pub fn LLVMIsAAllocaInst(value_ref: ValueRef) -> ValueRef;
|
||||
pub fn LLVMIsAConstantInt(value_ref: ValueRef) -> ValueRef;
|
||||
|
||||
pub fn LLVMInitializeX86TargetInfo();
|
||||
pub fn LLVMInitializeX86Target();
|
||||
|
|
|
@ -272,7 +272,7 @@ fn process_formals(&mut self, formals: &Vec<ast::Arg>, qualname: &str) {
|
|||
let typ =
|
||||
ppaux::ty_to_string(
|
||||
&self.analysis.ty_cx,
|
||||
*self.analysis.ty_cx.node_types.borrow().get(&id).unwrap());
|
||||
*self.analysis.ty_cx.node_types().get(&id).unwrap());
|
||||
// get the span only for the name of the variable (I hope the path is only ever a
|
||||
// variable name, but who knows?)
|
||||
self.fmt.formal_str(p.span,
|
||||
|
@ -436,7 +436,7 @@ fn process_struct_field_def(&mut self,
|
|||
let typ =
|
||||
ppaux::ty_to_string(
|
||||
&self.analysis.ty_cx,
|
||||
*self.analysis.ty_cx.node_types.borrow().get(&field.node.id).unwrap());
|
||||
*self.analysis.ty_cx.node_types().get(&field.node.id).unwrap());
|
||||
match self.span.sub_span_before_token(field.span, token::Colon) {
|
||||
Some(sub_span) => self.fmt.field_str(field.span,
|
||||
Some(sub_span),
|
||||
|
@ -1471,7 +1471,7 @@ fn visit_local(&mut self, l: &ast::Local) {
|
|||
|
||||
for &(id, ref p, ref immut, _) in &self.collected_paths {
|
||||
let value = if *immut { value.to_string() } else { "<mutable>".to_string() };
|
||||
let types = self.analysis.ty_cx.node_types.borrow();
|
||||
let types = self.analysis.ty_cx.node_types();
|
||||
let typ = ppaux::ty_to_string(&self.analysis.ty_cx, *types.get(&id).unwrap());
|
||||
// Get the span only for the name of the variable (I hope the path
|
||||
// is only ever a variable name, but who knows?).
|
||||
|
|
|
@ -963,6 +963,32 @@ pub fn const_to_uint(v: ValueRef) -> u64 {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_const_integral(v: ValueRef) -> bool {
|
||||
unsafe {
|
||||
!llvm::LLVMIsAConstantInt(v).is_null()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_to_opt_int(v: ValueRef) -> Option<i64> {
|
||||
unsafe {
|
||||
if is_const_integral(v) {
|
||||
Some(llvm::LLVMConstIntGetSExtValue(v))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn const_to_opt_uint(v: ValueRef) -> Option<u64> {
|
||||
unsafe {
|
||||
if is_const_integral(v) {
|
||||
Some(llvm::LLVMConstIntGetZExtValue(v))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_undef(val: ValueRef) -> bool {
|
||||
unsafe {
|
||||
llvm::LLVMIsUndef(val) != False
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr};
|
||||
use llvm::{InternalLinkage, ValueRef, Bool, True};
|
||||
use middle::{check_const, const_eval, def};
|
||||
use middle::const_eval::{const_int_checked_neg, const_uint_checked_neg};
|
||||
use middle::const_eval::{const_int_checked_add, const_uint_checked_add};
|
||||
use middle::const_eval::{const_int_checked_sub, const_uint_checked_sub};
|
||||
use middle::const_eval::{const_int_checked_mul, const_uint_checked_mul};
|
||||
use middle::const_eval::{const_int_checked_div, const_uint_checked_div};
|
||||
use middle::const_eval::{const_int_checked_rem, const_uint_checked_rem};
|
||||
use middle::const_eval::{const_int_checked_shl, const_uint_checked_shl};
|
||||
use middle::const_eval::{const_int_checked_shr, const_uint_checked_shr};
|
||||
use trans::{adt, closure, debuginfo, expr, inline, machine};
|
||||
use trans::base::{self, push_ctxt};
|
||||
use trans::common::*;
|
||||
|
@ -336,6 +344,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
let csize = machine::llsize_of_alloc(cx, val_ty(llconst));
|
||||
let tsize = machine::llsize_of_alloc(cx, llty);
|
||||
if csize != tsize {
|
||||
cx.sess().abort_if_errors();
|
||||
unsafe {
|
||||
// FIXME these values could use some context
|
||||
llvm::LLVMDumpValue(llconst);
|
||||
|
@ -348,6 +357,100 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
(llconst, ety_adjusted)
|
||||
}
|
||||
|
||||
fn check_unary_expr_validity(cx: &CrateContext, e: &ast::Expr, t: Ty,
|
||||
te: ValueRef) {
|
||||
// The only kind of unary expression that we check for validity
|
||||
// here is `-expr`, to check if it "overflows" (e.g. `-i32::MIN`).
|
||||
if let ast::ExprUnary(ast::UnNeg, ref inner_e) = e.node {
|
||||
|
||||
// An unfortunate special case: we parse e.g. -128 as a
|
||||
// negation of the literal 128, which means if we're expecting
|
||||
// a i8 (or if it was already suffixed, e.g. `-128_i8`), then
|
||||
// 128 will have already overflowed to -128, and so then the
|
||||
// constant evaluator thinks we're trying to negate -128.
|
||||
//
|
||||
// Catch this up front by looking for ExprLit directly,
|
||||
// and just accepting it.
|
||||
if let ast::ExprLit(_) = inner_e.node { return; }
|
||||
|
||||
let result = match t.sty {
|
||||
ty::ty_int(int_type) => {
|
||||
let input = match const_to_opt_int(te) {
|
||||
Some(v) => v,
|
||||
None => return,
|
||||
};
|
||||
const_int_checked_neg(
|
||||
input, e, Some(const_eval::IntTy::from(cx.tcx(), int_type)))
|
||||
}
|
||||
ty::ty_uint(uint_type) => {
|
||||
let input = match const_to_opt_uint(te) {
|
||||
Some(v) => v,
|
||||
None => return,
|
||||
};
|
||||
const_uint_checked_neg(
|
||||
input, e, Some(const_eval::UintTy::from(cx.tcx(), uint_type)))
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// We do not actually care about a successful result.
|
||||
if let Err(err) = result {
|
||||
cx.tcx().sess.span_err(e.span, &err.description());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_binary_expr_validity(cx: &CrateContext, e: &ast::Expr, t: Ty,
|
||||
te1: ValueRef, te2: ValueRef) {
|
||||
let b = if let ast::ExprBinary(b, _, _) = e.node { b } else { return };
|
||||
|
||||
let result = match t.sty {
|
||||
ty::ty_int(int_type) => {
|
||||
let (lhs, rhs) = match (const_to_opt_int(te1),
|
||||
const_to_opt_int(te2)) {
|
||||
(Some(v1), Some(v2)) => (v1, v2),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let opt_ety = Some(const_eval::IntTy::from(cx.tcx(), int_type));
|
||||
match b.node {
|
||||
ast::BiAdd => const_int_checked_add(lhs, rhs, e, opt_ety),
|
||||
ast::BiSub => const_int_checked_sub(lhs, rhs, e, opt_ety),
|
||||
ast::BiMul => const_int_checked_mul(lhs, rhs, e, opt_ety),
|
||||
ast::BiDiv => const_int_checked_div(lhs, rhs, e, opt_ety),
|
||||
ast::BiRem => const_int_checked_rem(lhs, rhs, e, opt_ety),
|
||||
ast::BiShl => const_int_checked_shl(lhs, rhs, e, opt_ety),
|
||||
ast::BiShr => const_int_checked_shr(lhs, rhs, e, opt_ety),
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
ty::ty_uint(uint_type) => {
|
||||
let (lhs, rhs) = match (const_to_opt_uint(te1),
|
||||
const_to_opt_uint(te2)) {
|
||||
(Some(v1), Some(v2)) => (v1, v2),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let opt_ety = Some(const_eval::UintTy::from(cx.tcx(), uint_type));
|
||||
match b.node {
|
||||
ast::BiAdd => const_uint_checked_add(lhs, rhs, e, opt_ety),
|
||||
ast::BiSub => const_uint_checked_sub(lhs, rhs, e, opt_ety),
|
||||
ast::BiMul => const_uint_checked_mul(lhs, rhs, e, opt_ety),
|
||||
ast::BiDiv => const_uint_checked_div(lhs, rhs, e, opt_ety),
|
||||
ast::BiRem => const_uint_checked_rem(lhs, rhs, e, opt_ety),
|
||||
ast::BiShl => const_uint_checked_shl(lhs, rhs, e, opt_ety),
|
||||
ast::BiShr => const_uint_checked_shr(lhs, rhs, e, opt_ety),
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
// We do not actually care about a successful result.
|
||||
if let Err(err) = result {
|
||||
cx.tcx().sess.span_err(e.span, &err.description());
|
||||
}
|
||||
}
|
||||
|
||||
fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
e: &ast::Expr,
|
||||
ety: Ty<'tcx>,
|
||||
|
@ -386,7 +489,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
let signed = ty::type_is_signed(intype);
|
||||
|
||||
let (te2, _) = const_expr(cx, &**e2, param_substs);
|
||||
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
|
||||
|
||||
check_binary_expr_validity(cx, e, ty, te1, te2);
|
||||
|
||||
match b.node {
|
||||
ast::BiAdd => {
|
||||
|
@ -416,8 +520,12 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
ast::BiBitXor => llvm::LLVMConstXor(te1, te2),
|
||||
ast::BiBitAnd => llvm::LLVMConstAnd(te1, te2),
|
||||
ast::BiBitOr => llvm::LLVMConstOr(te1, te2),
|
||||
ast::BiShl => llvm::LLVMConstShl(te1, te2),
|
||||
ast::BiShl => {
|
||||
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
|
||||
llvm::LLVMConstShl(te1, te2)
|
||||
}
|
||||
ast::BiShr => {
|
||||
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
|
||||
if signed { llvm::LLVMConstAShr(te1, te2) }
|
||||
else { llvm::LLVMConstLShr(te1, te2) }
|
||||
}
|
||||
|
@ -439,8 +547,11 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
}
|
||||
}
|
||||
},
|
||||
ast::ExprUnary(u, ref e) => {
|
||||
let (te, ty) = const_expr(cx, &**e, param_substs);
|
||||
ast::ExprUnary(u, ref inner_e) => {
|
||||
let (te, ty) = const_expr(cx, &**inner_e, param_substs);
|
||||
|
||||
check_unary_expr_validity(cx, e, ty, te);
|
||||
|
||||
let is_float = ty::type_is_fp(ty);
|
||||
match u {
|
||||
ast::UnUniq | ast::UnDeref => {
|
||||
|
@ -661,11 +772,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
ast::ExprRepeat(ref elem, ref count) => {
|
||||
let unit_ty = ty::sequence_element_type(cx.tcx(), ety);
|
||||
let llunitty = type_of::type_of(cx, unit_ty);
|
||||
let n = match const_eval::eval_const_expr_partial(cx.tcx(), &**count, None) {
|
||||
Ok(const_eval::const_int(i)) => i as usize,
|
||||
Ok(const_eval::const_uint(i)) => i as usize,
|
||||
_ => cx.sess().span_bug(count.span, "count must be integral const expression.")
|
||||
};
|
||||
let n = ty::eval_repeat_count(cx.tcx(), count);
|
||||
let unit_val = const_expr(cx, &**elem, param_substs).0;
|
||||
let vs: Vec<_> = repeat(unit_val).take(n).collect();
|
||||
if val_ty(unit_val) != llunitty {
|
||||
|
|
|
@ -3207,7 +3207,7 @@ fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
|
|||
fn assert_type_for_node_id(cx: &CrateContext,
|
||||
node_id: ast::NodeId,
|
||||
error_reporting_span: Span) {
|
||||
if !cx.tcx().node_types.borrow().contains_key(&node_id) {
|
||||
if !cx.tcx().node_types().contains_key(&node_id) {
|
||||
cx.sess().span_bug(error_reporting_span,
|
||||
"debuginfo: Could not find type for node id!");
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
use check::_match::pat_ctxt;
|
||||
use fmt_macros::{Parser, Piece, Position};
|
||||
use middle::astconv_util::{check_path_args, NO_TPS, NO_REGIONS};
|
||||
use middle::{const_eval, def};
|
||||
use middle::def;
|
||||
use middle::infer;
|
||||
use middle::mem_categorization as mc;
|
||||
use middle::mem_categorization::McResult;
|
||||
|
@ -94,7 +94,7 @@
|
|||
use middle::region::{self, CodeExtent};
|
||||
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace};
|
||||
use middle::traits;
|
||||
use middle::ty::{FnSig, GenericPredicates, VariantInfo, TypeScheme};
|
||||
use middle::ty::{FnSig, GenericPredicates, TypeScheme};
|
||||
use middle::ty::{Disr, ParamTy, ParameterEnvironment};
|
||||
use middle::ty::{self, HasProjectionTypes, RegionEscape, ToPolyTraitRef, Ty};
|
||||
use middle::ty::liberate_late_bound_regions;
|
||||
|
@ -4283,68 +4283,30 @@ fn int_in_range(ccx: &CrateCtxt, ty: ast::IntTy, disr: ty::Disr) -> bool {
|
|||
fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
vs: &'tcx [P<ast::Variant>],
|
||||
id: ast::NodeId,
|
||||
hint: attr::ReprAttr)
|
||||
-> Vec<Rc<ty::VariantInfo<'tcx>>> {
|
||||
hint: attr::ReprAttr) {
|
||||
#![allow(trivial_numeric_casts)]
|
||||
|
||||
let rty = ty::node_id_to_type(ccx.tcx, id);
|
||||
let mut variants: Vec<Rc<ty::VariantInfo>> = Vec::new();
|
||||
let mut disr_vals: Vec<ty::Disr> = Vec::new();
|
||||
let mut prev_disr_val: Option<ty::Disr> = None;
|
||||
|
||||
let inh = static_inherited_fields(ccx);
|
||||
let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), id);
|
||||
|
||||
let (_, repr_type_ty) = ty::enum_repr_type(ccx.tcx, Some(&hint));
|
||||
for v in vs {
|
||||
if let Some(ref e) = v.node.disr_expr {
|
||||
check_const_with_ty(&fcx, e.span, e, repr_type_ty);
|
||||
}
|
||||
}
|
||||
|
||||
// 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) => {
|
||||
if let Some(v) = prev_disr_val.checked_add(1) {
|
||||
v
|
||||
} else {
|
||||
ty::INITIAL_DISCRIMINANT_VALUE
|
||||
}
|
||||
}
|
||||
None => ty::INITIAL_DISCRIMINANT_VALUE
|
||||
};
|
||||
let def_id = local_def(id);
|
||||
|
||||
match v.node.disr_expr {
|
||||
Some(ref e) => {
|
||||
debug!("disr expr, checking {}", pprust::expr_to_string(&**e));
|
||||
// ty::enum_variants guards against discriminant overflows, so
|
||||
// we need not check for that.
|
||||
let variants = ty::enum_variants(ccx.tcx, def_id);
|
||||
|
||||
let inh = static_inherited_fields(ccx);
|
||||
let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id);
|
||||
let declty = match hint {
|
||||
attr::ReprAny | attr::ReprPacked |
|
||||
attr::ReprExtern => fcx.tcx().types.isize,
|
||||
|
||||
attr::ReprInt(_, attr::SignedInt(ity)) => {
|
||||
ty::mk_mach_int(fcx.tcx(), ity)
|
||||
}
|
||||
attr::ReprInt(_, attr::UnsignedInt(ity)) => {
|
||||
ty::mk_mach_uint(fcx.tcx(), ity)
|
||||
},
|
||||
};
|
||||
check_const_with_ty(&fcx, e.span, &**e, declty);
|
||||
// check_expr (from check_const pass) doesn't guarantee
|
||||
// that the expression is in a form that eval_const_expr can
|
||||
// handle, so we may still get an internal compiler error
|
||||
|
||||
match const_eval::eval_const_expr_partial(ccx.tcx, &**e, Some(declty)) {
|
||||
Ok(const_eval::const_int(val)) => current_disr_val = val as Disr,
|
||||
Ok(const_eval::const_uint(val)) => current_disr_val = val as Disr,
|
||||
Ok(_) => {
|
||||
span_err!(ccx.tcx.sess, e.span, E0079,
|
||||
"expected signed integer constant");
|
||||
}
|
||||
Err(ref err) => {
|
||||
span_err!(ccx.tcx.sess, err.span, E0080,
|
||||
"constant evaluation error: {}",
|
||||
err.description());
|
||||
}
|
||||
}
|
||||
},
|
||||
None => ()
|
||||
};
|
||||
for (v, variant) in vs.iter().zip(variants.iter()) {
|
||||
let current_disr_val = variant.disr_val;
|
||||
|
||||
// Check for duplicate discriminant values
|
||||
match disr_vals.iter().position(|&x| x == current_disr_val) {
|
||||
|
@ -4372,15 +4334,7 @@ fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
|||
}
|
||||
}
|
||||
disr_vals.push(current_disr_val);
|
||||
|
||||
let variant_info = Rc::new(VariantInfo::from_ast_variant(ccx.tcx, &**v,
|
||||
current_disr_val));
|
||||
prev_disr_val = Some(current_disr_val);
|
||||
|
||||
variants.push(variant_info);
|
||||
}
|
||||
|
||||
return variants;
|
||||
}
|
||||
|
||||
let hint = *ty::lookup_repr_hints(ccx.tcx, ast::DefId { krate: ast::LOCAL_CRATE, node: id })
|
||||
|
@ -4396,10 +4350,7 @@ fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
|||
};
|
||||
}
|
||||
|
||||
let variants = do_check(ccx, vs, id, hint);
|
||||
|
||||
// cache so that ty::enum_variants won't repeat this work
|
||||
ccx.tcx.enum_var_cache.borrow_mut().insert(local_def(id), Rc::new(variants));
|
||||
do_check(ccx, vs, id, hint);
|
||||
|
||||
check_representable(ccx.tcx, sp, id, "enum");
|
||||
|
||||
|
|
|
@ -51,8 +51,6 @@
|
|||
E0075,
|
||||
E0076,
|
||||
E0077,
|
||||
E0079,
|
||||
E0080,
|
||||
E0081,
|
||||
E0082,
|
||||
E0083,
|
||||
|
|
|
@ -146,7 +146,7 @@ pub struct CrateCtxt<'a, 'tcx: 'a> {
|
|||
fn write_ty_to_tcx<'tcx>(tcx: &ty::ctxt<'tcx>, node_id: ast::NodeId, ty: Ty<'tcx>) {
|
||||
debug!("write_ty_to_tcx({}, {})", node_id, ppaux::ty_to_string(tcx, ty));
|
||||
assert!(!ty::type_needs_infer(ty));
|
||||
tcx.node_types.borrow_mut().insert(node_id, ty);
|
||||
tcx.node_type_insert(node_id, ty);
|
||||
}
|
||||
|
||||
fn write_substs_to_tcx<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
|
|
|
@ -519,7 +519,8 @@ macro_rules! u64_from_be_bytes_bench_impl {
|
|||
({
|
||||
use super::u64_from_be_bytes;
|
||||
|
||||
let data = (0..$stride*100+$start_index).collect::<Vec<_>>();
|
||||
let len = $stride.wrapping_mul(100).wrapping_add($start_index);
|
||||
let data = (0..len).collect::<Vec<_>>();
|
||||
let mut sum = 0;
|
||||
$b.iter(|| {
|
||||
let mut i = $start_index;
|
||||
|
|
30
src/test/compile-fail/const-eval-overflow-2.rs
Normal file
30
src/test/compile-fail/const-eval-overflow-2.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Evaluation of constants in refutable patterns goes through
|
||||
// different compiler control-flow paths.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const NEG_128: i8 = -128;
|
||||
const NEG_NEG_128: i8 = -NEG_128;
|
||||
//~^ ERROR constant evaluation error: attempted to negate with overflow
|
||||
//~| ERROR attempted to negate with overflow
|
||||
|
||||
fn main() {
|
||||
match -128i8 {
|
||||
NEG_NEG_128 => println!("A"),
|
||||
_ => println!("B"),
|
||||
}
|
||||
}
|
39
src/test/compile-fail/const-eval-overflow-3.rs
Normal file
39
src/test/compile-fail/const-eval-overflow-3.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Evaluation of constants in array-elem count goes through different
|
||||
// compiler control-flow paths.
|
||||
//
|
||||
// This test is checking the count in an array expression.
|
||||
|
||||
// FIXME (#23926): the error output is not consistent between a
|
||||
// self-hosted and a cross-compiled setup; therefore resorting to
|
||||
// error-pattern for now.
|
||||
|
||||
// error-pattern: expected constant integer for repeat count, but attempted to add with overflow
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const A_I8_I
|
||||
: [u32; (i8::MAX as usize) + 1]
|
||||
= [0; (i8::MAX + 1) as usize];
|
||||
|
||||
fn main() {
|
||||
foo(&A_I8_I[..]);
|
||||
}
|
||||
|
||||
fn foo<T:fmt::Debug>(x: T) {
|
||||
println!("{:?}", x);
|
||||
}
|
||||
|
43
src/test/compile-fail/const-eval-overflow-3b.rs
Normal file
43
src/test/compile-fail/const-eval-overflow-3b.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Evaluation of constants in array-elem count goes through different
|
||||
// compiler control-flow paths.
|
||||
//
|
||||
// This test is checking the count in an array expression.
|
||||
//
|
||||
// This is a variation of another such test, but in this case the
|
||||
// types for the left- and right-hand sides of the addition do not
|
||||
// match (as well as overflow).
|
||||
|
||||
// FIXME (#23926): the error output is not consistent between a
|
||||
// self-hosted and a cross-compiled setup; therefore resorting to
|
||||
// error-pattern for now.
|
||||
|
||||
// error-pattern: mismatched types
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const A_I8_I
|
||||
: [u32; (i8::MAX as usize) + 1]
|
||||
= [0; (i8::MAX + 1u8) as usize];
|
||||
|
||||
fn main() {
|
||||
foo(&A_I8_I[..]);
|
||||
}
|
||||
|
||||
fn foo<T:fmt::Debug>(x: T) {
|
||||
println!("{:?}", x);
|
||||
}
|
||||
|
36
src/test/compile-fail/const-eval-overflow-4.rs
Normal file
36
src/test/compile-fail/const-eval-overflow-4.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ignore-test this should fail to compile (#23833)
|
||||
|
||||
// Evaluation of constants in array-elem count goes through different
|
||||
// compiler control-flow paths.
|
||||
//
|
||||
// This test is checking the count in an array type.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const A_I8_T
|
||||
: [u32; (i8::MAX as i8 + 1i8) as usize]
|
||||
//~^ ERROR error evaluating count: attempted to add with overflow
|
||||
= [0; (i8::MAX as usize) + 1];
|
||||
|
||||
fn main() {
|
||||
foo(&A_I8_T[..]);
|
||||
}
|
||||
|
||||
fn foo<T:fmt::Debug>(x: T) {
|
||||
println!("{:?}", x);
|
||||
}
|
||||
|
36
src/test/compile-fail/const-eval-overflow-4b.rs
Normal file
36
src/test/compile-fail/const-eval-overflow-4b.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Evaluation of constants in array-elem count goes through different
|
||||
// compiler control-flow paths.
|
||||
//
|
||||
// This test is checking the count in an array type.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const A_I8_T
|
||||
: [u32; (i8::MAX as i8 + 1u8) as usize]
|
||||
//~^ ERROR mismatched types
|
||||
//~| the trait `core::ops::Add<u8>` is not implemented for the type `i8`
|
||||
//~| the trait `core::ops::Add<u8>` is not implemented for the type `i8`
|
||||
= [0; (i8::MAX as usize) + 1];
|
||||
|
||||
fn main() {
|
||||
foo(&A_I8_T[..]);
|
||||
}
|
||||
|
||||
fn foo<T:fmt::Debug>(x: T) {
|
||||
println!("{:?}", x);
|
||||
}
|
||||
|
129
src/test/compile-fail/const-eval-overflow.rs
Normal file
129
src/test/compile-fail/const-eval-overflow.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
// 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
|
||||
// change this warn to a deny, then the compiler will exit before
|
||||
// those errors are detected.
|
||||
#![warn(unsigned_negation)]
|
||||
|
||||
use std::fmt;
|
||||
use std::{i8, i16, i32, i64, isize};
|
||||
use std::{u8, u16, u32, u64, usize};
|
||||
|
||||
const VALS_I8: (i8, i8, i8, i8) =
|
||||
(-i8::MIN,
|
||||
//~^ ERROR attempted to negate with overflow
|
||||
i8::MIN - 1,
|
||||
//~^ ERROR attempted to sub with overflow
|
||||
i8::MAX + 1,
|
||||
//~^ ERROR attempted to add with overflow
|
||||
i8::MIN * 2,
|
||||
//~^ ERROR attempted to mul with overflow
|
||||
);
|
||||
|
||||
const VALS_I16: (i16, i16, i16, i16) =
|
||||
(-i16::MIN,
|
||||
//~^ ERROR attempted to negate with overflow
|
||||
i16::MIN - 1,
|
||||
//~^ ERROR attempted to sub with overflow
|
||||
i16::MAX + 1,
|
||||
//~^ ERROR attempted to add with overflow
|
||||
i16::MIN * 2,
|
||||
//~^ ERROR attempted to mul with overflow
|
||||
);
|
||||
|
||||
const VALS_I32: (i32, i32, i32, i32) =
|
||||
(-i32::MIN,
|
||||
//~^ ERROR attempted to negate with overflow
|
||||
i32::MIN - 1,
|
||||
//~^ ERROR attempted to sub with overflow
|
||||
i32::MAX + 1,
|
||||
//~^ ERROR attempted to add with overflow
|
||||
i32::MIN * 2,
|
||||
//~^ ERROR attempted to mul with overflow
|
||||
);
|
||||
|
||||
const VALS_I64: (i64, i64, i64, i64) =
|
||||
(-i64::MIN,
|
||||
//~^ ERROR attempted to negate with overflow
|
||||
i64::MIN - 1,
|
||||
//~^ ERROR attempted to sub with overflow
|
||||
i64::MAX + 1,
|
||||
//~^ ERROR attempted to add with overflow
|
||||
i64::MAX * 2,
|
||||
//~^ ERROR attempted to mul with overflow
|
||||
);
|
||||
|
||||
const VALS_U8: (u8, u8, u8, u8) =
|
||||
(-u8::MIN,
|
||||
//~^ WARNING negation of unsigned int variable may be unintentional
|
||||
// (The above is separately linted; unsigned negation is defined to be !x+1.)
|
||||
u8::MIN - 1,
|
||||
//~^ ERROR attempted to sub with overflow
|
||||
u8::MAX + 1,
|
||||
//~^ ERROR attempted to add with overflow
|
||||
u8::MAX * 2,
|
||||
//~^ ERROR attempted to mul with overflow
|
||||
);
|
||||
|
||||
const VALS_U16: (u16, u16, u16, u16) =
|
||||
(-u16::MIN,
|
||||
//~^ WARNING negation of unsigned int variable may be unintentional
|
||||
// (The above is separately linted; unsigned negation is defined to be !x+1.)
|
||||
u16::MIN - 1,
|
||||
//~^ ERROR attempted to sub with overflow
|
||||
u16::MAX + 1,
|
||||
//~^ ERROR attempted to add with overflow
|
||||
u16::MAX * 2,
|
||||
//~^ ERROR attempted to mul with overflow
|
||||
);
|
||||
|
||||
const VALS_U32: (u32, u32, u32, u32) =
|
||||
(-u32::MIN,
|
||||
//~^ WARNING negation of unsigned int variable may be unintentional
|
||||
// (The above is separately linted; unsigned negation is defined to be !x+1.)
|
||||
u32::MIN - 1,
|
||||
//~^ ERROR attempted to sub with overflow
|
||||
u32::MAX + 1,
|
||||
//~^ ERROR attempted to add with overflow
|
||||
u32::MAX * 2,
|
||||
//~^ ERROR attempted to mul with overflow
|
||||
);
|
||||
|
||||
const VALS_U64: (u64, u64, u64, u64) =
|
||||
(-u64::MIN,
|
||||
//~^ WARNING negation of unsigned int variable may be unintentional
|
||||
// (The above is separately linted; unsigned negation is defined to be !x+1.)
|
||||
u64::MIN - 1,
|
||||
//~^ ERROR attempted to sub with overflow
|
||||
u64::MAX + 1,
|
||||
//~^ ERROR attempted to add with overflow
|
||||
u64::MAX * 2,
|
||||
//~^ ERROR attempted to mul with overflow
|
||||
);
|
||||
|
||||
fn main() {
|
||||
foo(VALS_I8);
|
||||
foo(VALS_I16);
|
||||
foo(VALS_I32);
|
||||
foo(VALS_I64);
|
||||
|
||||
foo(VALS_U8);
|
||||
foo(VALS_U16);
|
||||
foo(VALS_U32);
|
||||
foo(VALS_U64);
|
||||
}
|
||||
|
||||
fn foo<T:fmt::Debug>(x: T) {
|
||||
println!("{:?}", x);
|
||||
}
|
118
src/test/compile-fail/discrim-ill-typed.rs
Normal file
118
src/test/compile-fail/discrim-ill-typed.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// When explicit discriminant value has
|
||||
// a type that does not match the representation
|
||||
// type, rustc should fail gracefully.
|
||||
|
||||
// See also run-pass/discrim-explicit-23030.rs where the input types
|
||||
// are correct.
|
||||
|
||||
#![allow(dead_code, unused_variables, unused_imports)]
|
||||
|
||||
use std::{i8,u8,i16,u16,i32,u32,i64, u64};
|
||||
|
||||
fn f_i8() {
|
||||
#[repr(i8)]
|
||||
enum A {
|
||||
Ok = i8::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = 0_u8,
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_u8() {
|
||||
#[repr(u8)]
|
||||
enum A {
|
||||
Ok = u8::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = 0_i8,
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_i16() {
|
||||
#[repr(i16)]
|
||||
enum A {
|
||||
Ok = i16::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = 0_u16,
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_u16() {
|
||||
#[repr(u16)]
|
||||
enum A {
|
||||
Ok = u16::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = 0_i16,
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_i32() {
|
||||
#[repr(i32)]
|
||||
enum A {
|
||||
Ok = i32::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = 0_u32,
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_u32() {
|
||||
#[repr(u32)]
|
||||
enum A {
|
||||
Ok = u32::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = 0_i32,
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_i64() {
|
||||
#[repr(i64)]
|
||||
enum A {
|
||||
Ok = i64::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = 0_u64,
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_u64() {
|
||||
#[repr(u64)]
|
||||
enum A {
|
||||
Ok = u64::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = 0_i64,
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn main() { }
|
94
src/test/compile-fail/discrim-overflow-2.rs
Normal file
94
src/test/compile-fail/discrim-overflow-2.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ignore-tidy-linelength
|
||||
|
||||
// Issue 23030: Detect overflowing discriminant
|
||||
//
|
||||
// Check that we detect the overflow even if enum is not used.
|
||||
|
||||
// See also run-pass/discrim-explicit-23030.rs where the suggested
|
||||
// workaround is tested.
|
||||
|
||||
use std::{i8,u8,i16,u16,i32,u32,i64, u64};
|
||||
|
||||
fn f_i8() {
|
||||
#[repr(i8)]
|
||||
enum A {
|
||||
Ok = i8::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed on value after 127: i8; set explicitly via OhNo = -128 if that is desired outcome
|
||||
}
|
||||
}
|
||||
|
||||
fn f_u8() {
|
||||
#[repr(u8)]
|
||||
enum A {
|
||||
Ok = u8::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed on value after 255: u8; set explicitly via OhNo = 0 if that is desired outcome
|
||||
}
|
||||
}
|
||||
|
||||
fn f_i16() {
|
||||
#[repr(i16)]
|
||||
enum A {
|
||||
Ok = i16::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
}
|
||||
|
||||
fn f_u16() {
|
||||
#[repr(u16)]
|
||||
enum A {
|
||||
Ok = u16::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
}
|
||||
|
||||
fn f_i32() {
|
||||
#[repr(i32)]
|
||||
enum A {
|
||||
Ok = i32::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
}
|
||||
|
||||
fn f_u32() {
|
||||
#[repr(u32)]
|
||||
enum A {
|
||||
Ok = u32::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
}
|
||||
|
||||
fn f_i64() {
|
||||
#[repr(i64)]
|
||||
enum A {
|
||||
Ok = i64::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
}
|
||||
|
||||
fn f_u64() {
|
||||
#[repr(u64)]
|
||||
enum A {
|
||||
Ok = u64::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
}
|
||||
|
||||
fn main() { }
|
108
src/test/compile-fail/discrim-overflow.rs
Normal file
108
src/test/compile-fail/discrim-overflow.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ignore-tidy-linelength
|
||||
|
||||
// Issue 23030: Detect overflowing discriminant
|
||||
|
||||
// See also run-pass/discrim-explicit-23030.rs where the suggested
|
||||
// workaround is tested.
|
||||
|
||||
use std::{i8,u8,i16,u16,i32,u32,i64, u64};
|
||||
|
||||
fn f_i8() {
|
||||
#[repr(i8)]
|
||||
enum A {
|
||||
Ok = i8::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed on value after 127: i8; set explicitly via OhNo = -128 if that is desired outcome
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_u8() {
|
||||
#[repr(u8)]
|
||||
enum A {
|
||||
Ok = u8::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed on value after 255: u8; set explicitly via OhNo = 0 if that is desired outcome
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_i16() {
|
||||
#[repr(i16)]
|
||||
enum A {
|
||||
Ok = i16::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_u16() {
|
||||
#[repr(u16)]
|
||||
enum A {
|
||||
Ok = u16::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_i32() {
|
||||
#[repr(i32)]
|
||||
enum A {
|
||||
Ok = i32::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_u32() {
|
||||
#[repr(u32)]
|
||||
enum A {
|
||||
Ok = u32::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_i64() {
|
||||
#[repr(i64)]
|
||||
enum A {
|
||||
Ok = i64::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn f_u64() {
|
||||
#[repr(u64)]
|
||||
enum A {
|
||||
Ok = u64::MAX - 1,
|
||||
Ok2,
|
||||
OhNo, //~ ERROR enum discriminant overflowed
|
||||
}
|
||||
|
||||
let x = A::Ok;
|
||||
}
|
||||
|
||||
fn main() { }
|
|
@ -8,9 +8,14 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// error-pattern: too big for the current
|
||||
// FIXME (#23926): the error output is not consistent between a
|
||||
// self-hosted and a cross-compiled setup. Skipping for now.
|
||||
|
||||
// ignore-test FIXME(#23926)
|
||||
|
||||
#![allow(exceeding_bitshifts)]
|
||||
|
||||
fn main() {
|
||||
let fat : [u8; (1<<61)+(1<<31)] = [0; (1u64<<61) as usize +(1u64<<31) as usize];
|
||||
let _fat : [u8; (1<<61)+(1<<31)] =
|
||||
[0; (1u64<<61) as usize +(1u64<<31) as usize];
|
||||
}
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Note: This test is checking that we forbid a coding pattern that
|
||||
// Issue #5873 explicitly wants to allow.
|
||||
|
||||
enum State { ST_NULL, ST_WHITESPACE }
|
||||
|
||||
fn main() {
|
||||
[State::ST_NULL; (State::ST_WHITESPACE as usize)];
|
||||
//~^ ERROR expected constant integer for repeat count, found non-constant expression
|
||||
//~^ ERROR expected constant integer for repeat count, but non-constant path
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
fn main() {
|
||||
fn bar(n: usize) {
|
||||
let _x = [0; n]; //~ ERROR expected constant integer for repeat count, found variable
|
||||
let _x = [0; n];
|
||||
//~^ ERROR expected constant integer for repeat count, found variable
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,16 @@
|
|||
|
||||
// pretty-expanded FIXME #23616
|
||||
|
||||
#![feature(core)]
|
||||
|
||||
// Catch mistakes in the overflowing literals lint.
|
||||
#![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!(-2147483648 - 1, 2147483647);
|
||||
assert_eq!((-2147483648).wrapping_sub(1), 2147483647);
|
||||
}
|
||||
|
|
156
src/test/run-pass/discrim-explicit-23030.rs
Normal file
156
src/test/run-pass/discrim-explicit-23030.rs
Normal file
|
@ -0,0 +1,156 @@
|
|||
// 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Issue 23030: Workaround overflowing discriminant
|
||||
// with explicit assignments.
|
||||
|
||||
// See also compile-fail/overflow-discrim.rs, which shows what
|
||||
// happens if you leave the OhNo explicit cases out here.
|
||||
|
||||
use std::{i8,u8,i16,u16,i32,u32,i64,u64,isize,usize};
|
||||
|
||||
fn f_i8() {
|
||||
#[repr(i8)]
|
||||
enum A {
|
||||
Ok = i8::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = i8::MIN,
|
||||
NotTheEnd = -1,
|
||||
Zero,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
let z = (A::NotTheEnd, A::Zero).1 as i8;
|
||||
assert_eq!(z, 0);
|
||||
}
|
||||
|
||||
fn f_u8() {
|
||||
#[repr(u8)]
|
||||
enum A {
|
||||
Ok = u8::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = u8::MIN,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
}
|
||||
|
||||
fn f_i16() {
|
||||
#[repr(i16)]
|
||||
enum A {
|
||||
Ok = i16::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = i16::MIN,
|
||||
NotTheEnd = -1,
|
||||
Zero,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
let z = (A::NotTheEnd, A::Zero).1 as i16;
|
||||
assert_eq!(z, 0);
|
||||
}
|
||||
|
||||
fn f_u16() {
|
||||
#[repr(u16)]
|
||||
enum A {
|
||||
Ok = u16::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = u16::MIN,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
}
|
||||
|
||||
fn f_i32() {
|
||||
#[repr(i32)]
|
||||
enum A {
|
||||
Ok = i32::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = i32::MIN,
|
||||
NotTheEnd = -1,
|
||||
Zero,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
let z = (A::NotTheEnd, A::Zero).1 as i32;
|
||||
assert_eq!(z, 0);
|
||||
}
|
||||
|
||||
fn f_u32() {
|
||||
#[repr(u32)]
|
||||
enum A {
|
||||
Ok = u32::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = u32::MIN,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
}
|
||||
|
||||
fn f_i64() {
|
||||
#[repr(i64)]
|
||||
enum A {
|
||||
Ok = i64::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = i64::MIN,
|
||||
NotTheEnd = -1,
|
||||
Zero,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
let z = (A::NotTheEnd, A::Zero).1 as i64;
|
||||
assert_eq!(z, 0);
|
||||
}
|
||||
|
||||
fn f_u64() {
|
||||
#[repr(u64)]
|
||||
enum A {
|
||||
Ok = u64::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = u64::MIN,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
}
|
||||
|
||||
fn f_isize() {
|
||||
#[repr(isize)]
|
||||
enum A {
|
||||
Ok = isize::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = isize::MIN,
|
||||
NotTheEnd = -1,
|
||||
Zero,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
let z = (A::NotTheEnd, A::Zero).1 as isize;
|
||||
assert_eq!(z, 0);
|
||||
}
|
||||
|
||||
fn f_usize() {
|
||||
#[repr(usize)]
|
||||
enum A {
|
||||
Ok = usize::MAX - 1,
|
||||
Ok2,
|
||||
OhNo = usize::MIN,
|
||||
}
|
||||
|
||||
let _x = (A::Ok, A::Ok2, A::OhNo);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f_i8(); f_u8();
|
||||
f_i16(); f_u16();
|
||||
f_i32(); f_u32();
|
||||
f_i64(); f_u64();
|
||||
|
||||
f_isize(); f_usize();
|
||||
}
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
// pretty-expanded FIXME #23616
|
||||
|
||||
// this is for the wrapping_add call below.
|
||||
#![feature(core)]
|
||||
|
||||
/*!
|
||||
* Tests the range assertion wraparound case in trans::middle::adt::load_discr.
|
||||
*/
|
||||
|
@ -29,8 +32,8 @@ enum Es { Ls = -128, Hs = 127 }
|
|||
static CHs: Es = Es::Hs;
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!((Eu::Hu as u8) + 1, Eu::Lu as u8);
|
||||
assert_eq!((Es::Hs as i8) + 1, Es::Ls as i8);
|
||||
assert_eq!((Eu::Hu as u8).wrapping_add(1), Eu::Lu as u8);
|
||||
assert_eq!((Es::Hs as i8).wrapping_add(1), Es::Ls as i8);
|
||||
assert_eq!(CLu as u8, Eu::Lu as u8);
|
||||
assert_eq!(CHu as u8, Eu::Hu as u8);
|
||||
assert_eq!(CLs as i8, Es::Ls as i8);
|
||||
|
|
Loading…
Reference in a new issue