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:
Alex Crichton 2015-03-31 18:06:35 -07:00
commit 4f643d79fc
30 changed files with 1733 additions and 267 deletions

View file

@ -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)
}
}

View file

@ -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]

View file

@ -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 }

View file

@ -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 {

View file

@ -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),
}
}

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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?).

View file

@ -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

View file

@ -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 {

View file

@ -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!");
}

View file

@ -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");

View file

@ -51,8 +51,6 @@
E0075,
E0076,
E0077,
E0079,
E0080,
E0081,
E0082,
E0083,

View file

@ -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>,

View file

@ -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;

View 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"),
}
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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() { }

View 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() { }

View 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() { }

View file

@ -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];
}

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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);
}

View 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();
}

View file

@ -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);