From 7bde56e14941924c17daeaa1996d61d0ffdb6bd1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 16 Dec 2015 18:44:15 +0100 Subject: [PATCH 01/13] typestrong constant integers --- mk/crates.mk | 16 +- src/librustc/lib.rs | 1 + src/librustc/middle/const_eval.rs | 699 +++++++----------- src/librustc/middle/ty/mod.rs | 5 +- src/librustc/middle/ty/util.rs | 171 ++--- src/librustc/mir/repr.rs | 11 +- src/librustc/mir/tcx.rs | 11 +- src/librustc_const_eval/err.rs | 85 +++ src/librustc_const_eval/int.rs | 551 ++++++++++++++ src/librustc_const_eval/is.rs | 39 + src/librustc_const_eval/lib.rs | 42 ++ src/librustc_const_eval/us.rs | 39 + src/librustc_lint/types.rs | 7 +- src/librustc_metadata/decoder.rs | 8 +- src/librustc_metadata/encoder.rs | 21 +- src/librustc_metadata/lib.rs | 1 + src/librustc_mir/build/matches/mod.rs | 2 +- src/librustc_mir/build/matches/test.rs | 2 +- src/librustc_mir/build/misc.rs | 2 +- src/librustc_mir/build/scope.rs | 6 +- src/librustc_mir/hair/cx/expr.rs | 41 +- src/librustc_mir/hair/cx/mod.rs | 8 +- src/librustc_mir/lib.rs | 1 + src/librustc_trans/lib.rs | 1 + src/librustc_trans/trans/adt.rs | 2 +- src/librustc_trans/trans/consts.rs | 144 ++-- .../trans/debuginfo/metadata.rs | 2 +- src/librustc_trans/trans/disr.rs | 2 +- src/librustc_trans/trans/mir/constant.rs | 25 +- src/librustc_trans/trans/mir/rvalue.rs | 5 +- src/librustc_typeck/astconv.rs | 28 +- src/librustc_typeck/check/mod.rs | 52 +- src/librustc_typeck/collect.rs | 71 +- src/librustc_typeck/lib.rs | 1 + src/test/auxiliary/dummy_mir_pass.rs | 8 +- src/test/compile-fail/const-err.rs | 4 +- .../compile-fail/const-eval-overflow-2.rs | 3 +- .../compile-fail/const-eval-overflow-3.rs | 3 +- .../compile-fail/const-eval-overflow-4b.rs | 3 +- src/test/compile-fail/const-eval-overflow.rs | 32 +- .../compile-fail/const-integer-bool-ops.rs | 22 +- .../const-len-underflow-separate-spans.rs | 2 +- .../const-len-underflow-subspans.rs | 2 +- src/test/compile-fail/const-tup-index-span.rs | 2 +- src/test/compile-fail/discrim-overflow-2.rs | 4 +- src/test/compile-fail/discrim-overflow.rs | 4 +- .../compile-fail/enum-discrim-too-small.rs | 36 +- .../compile-fail/enum-discrim-too-small2.rs | 44 ++ src/test/compile-fail/eval-enum.rs | 3 +- .../feature-gate-negate-unsigned.rs | 19 +- .../feature-gate-negate-unsigned0.rs | 31 + src/test/compile-fail/issue-15524.rs | 6 +- src/test/compile-fail/issue-8460-const.rs | 20 +- src/test/compile-fail/issue-8761.rs | 8 +- src/test/compile-fail/lint-type-limits.rs | 13 - src/test/compile-fail/lint-type-limits2.rs | 23 + src/test/compile-fail/lint-type-limits3.rs | 21 + src/test/compile-fail/lint-type-overflow.rs | 6 - src/test/compile-fail/lint-type-overflow2.rs | 22 + src/test/compile-fail/repeat_count.rs | 4 +- src/test/run-pass/const-fn.rs | 12 +- src/test/run-pass/const-negation.rs | 37 + src/test/run-pass/issue-23833.rs | 25 + .../run-pass/packed-struct-generic-layout.rs | 4 +- src/test/run-pass/shift.rs | 6 +- 65 files changed, 1614 insertions(+), 917 deletions(-) create mode 100644 src/librustc_const_eval/err.rs create mode 100644 src/librustc_const_eval/int.rs create mode 100644 src/librustc_const_eval/is.rs create mode 100644 src/librustc_const_eval/lib.rs create mode 100644 src/librustc_const_eval/us.rs create mode 100644 src/test/compile-fail/enum-discrim-too-small2.rs create mode 100644 src/test/compile-fail/feature-gate-negate-unsigned0.rs create mode 100644 src/test/compile-fail/lint-type-limits2.rs create mode 100644 src/test/compile-fail/lint-type-limits3.rs create mode 100644 src/test/compile-fail/lint-type-overflow2.rs create mode 100644 src/test/run-pass/const-negation.rs create mode 100644 src/test/run-pass/issue-23833.rs diff --git a/mk/crates.mk b/mk/crates.mk index 1ecceb9280a7c..c4ab068f39be6 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -53,7 +53,7 @@ TARGET_CRATES := libc std term \ getopts collections test rand \ core alloc \ rustc_unicode rustc_bitflags \ - alloc_system alloc_jemalloc + alloc_system alloc_jemalloc rustc_const_eval RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_driver \ rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \ rustc_data_structures rustc_front rustc_platform_intrinsics \ @@ -91,8 +91,11 @@ DEPS_test := std getopts term native:rust_test_helpers DEPS_syntax := std term serialize log arena libc rustc_bitflags rustc_unicode DEPS_syntax_ext := syntax fmt_macros +DEPS_rustc_const_eval := std syntax + DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml rustc_front\ - log graphviz rustc_llvm rustc_back rustc_data_structures + log graphviz rustc_llvm rustc_back rustc_data_structures\ + rustc_const_eval DEPS_rustc_back := std syntax rustc_llvm rustc_front flate log libc DEPS_rustc_borrowck := rustc rustc_front log graphviz syntax DEPS_rustc_data_structures := std log serialize @@ -103,16 +106,17 @@ DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_bo DEPS_rustc_front := std syntax log serialize DEPS_rustc_lint := rustc log syntax DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags -DEPS_rustc_metadata := rustc rustc_front syntax rbml +DEPS_rustc_metadata := rustc rustc_front syntax rbml rustc_const_eval DEPS_rustc_passes := syntax rustc core rustc_front -DEPS_rustc_mir := rustc rustc_front syntax +DEPS_rustc_mir := rustc rustc_front syntax rustc_const_eval DEPS_rustc_resolve := arena rustc rustc_front log syntax DEPS_rustc_platform_intrinsics := rustc rustc_llvm DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir DEPS_rustc_privacy := rustc rustc_front log syntax DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \ - log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics -DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics + log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics \ + rustc_const_eval +DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics rustc_const_eval DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \ test rustc_lint rustc_front diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 4ff3b21dc83f4..3988545c201e0 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -55,6 +55,7 @@ extern crate rustc_front; extern crate rustc_data_structures; extern crate serialize; extern crate collections; +extern crate rustc_const_eval; #[macro_use] extern crate log; #[macro_use] extern crate syntax; #[macro_use] #[no_link] extern crate rustc_bitflags; diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 06030c0021171..c3f8d7bf9598e 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -23,10 +23,9 @@ use middle::subst::Subst; use middle::def_id::DefId; use middle::pat_util::def_to_path; use middle::ty::{self, Ty, TyCtxt}; +use middle::ty::util::IntTypeExt; use middle::astconv_util::ast_ty_to_prim_ty; -use util::num::ToPrimitive; use util::nodemap::NodeMap; -use session::Session; use graphviz::IntoCow; use syntax::ast; @@ -43,10 +42,20 @@ use std::cmp::Ordering; use std::collections::hash_map::Entry::Vacant; use std::hash; use std::mem::transmute; -use std::{i8, i16, i32, i64, u8, u16, u32, u64}; use std::rc::Rc; -fn lookup_variant_by_id<'a>(tcx: &'a TyCtxt, +use rustc_const_eval::*; + +macro_rules! math { + ($e:expr, $op:expr) => { + match $op { + Ok(val) => val, + Err(e) => signal!($e, Math(e)), + } + } +} + +fn lookup_variant_by_id<'a>(tcx: &'a ty::TyCtxt, enum_def: DefId, variant_def: DefId) -> Option<&'a Expr> { @@ -248,8 +257,7 @@ pub fn lookup_const_fn_by_id<'tcx>(tcx: &TyCtxt<'tcx>, def_id: DefId) #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum ConstVal { Float(f64), - Int(i64), - Uint(u64), + Integral(ConstInt), Str(InternedString), ByteStr(Rc>), Bool(bool), @@ -258,14 +266,14 @@ pub enum ConstVal { Function(DefId), Array(ast::NodeId, u64), Repeat(ast::NodeId, u64), + Char(char), } impl hash::Hash for ConstVal { fn hash(&self, state: &mut H) { match *self { Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state), - Int(a) => a.hash(state), - Uint(a) => a.hash(state), + Integral(a) => a.hash(state), Str(ref a) => a.hash(state), ByteStr(ref a) => a.hash(state), Bool(a) => a.hash(state), @@ -274,6 +282,7 @@ impl hash::Hash for ConstVal { Function(a) => a.hash(state), Array(a, n) => { a.hash(state); n.hash(state) }, Repeat(a, n) => { a.hash(state); n.hash(state) }, + Char(c) => c.hash(state), } } } @@ -286,8 +295,7 @@ impl PartialEq for ConstVal { fn eq(&self, other: &ConstVal) -> bool { match (self, other) { (&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}, - (&Int(a), &Int(b)) => a == b, - (&Uint(a), &Uint(b)) => a == b, + (&Integral(a), &Integral(b)) => a == b, (&Str(ref a), &Str(ref b)) => a == b, (&ByteStr(ref a), &ByteStr(ref b)) => a == b, (&Bool(a), &Bool(b)) => a == b, @@ -296,6 +304,7 @@ impl PartialEq for ConstVal { (&Function(a), &Function(b)) => a == b, (&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn), (&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn), + (&Char(a), &Char(b)) => a == b, _ => false, } } @@ -307,9 +316,7 @@ impl ConstVal { pub fn description(&self) -> &'static str { match *self { Float(_) => "float", - Int(i) if i < 0 => "negative integer", - Int(_) => "positive integer", - Uint(_) => "unsigned integer", + Integral(i) => i.description(), Str(_) => "string literal", ByteStr(_) => "byte string literal", Bool(_) => "boolean", @@ -318,6 +325,7 @@ impl ConstVal { Function(_) => "function definition", Array(..) => "array", Repeat(..) => "repeat", + Char(..) => "char", } } } @@ -402,7 +410,6 @@ pub enum ErrKind { CannotCast, CannotCastTo(&'static str), InvalidOpForInts(hir::BinOp_), - InvalidOpForUInts(hir::BinOp_), InvalidOpForBools(hir::BinOp_), InvalidOpForFloats(hir::BinOp_), InvalidOpForIntUint(hir::BinOp_), @@ -442,6 +449,13 @@ pub enum ErrKind { MiscCatchAll, IndexOpFeatureGated, + Math(ConstMathErr), +} + +impl From for ErrKind { + fn from(err: ConstMathErr) -> ErrKind { + Math(err) + } } impl ConstEvalErr { @@ -451,8 +465,7 @@ impl ConstEvalErr { match self.kind { CannotCast => "can't cast this type".into_cow(), CannotCastTo(s) => format!("can't cast this type to {}", s).into_cow(), - InvalidOpForInts(_) => "can't do this op on signed integrals".into_cow(), - InvalidOpForUInts(_) => "can't do this op on unsigned integrals".into_cow(), + InvalidOpForInts(_) => "can't do this op on integrals".into_cow(), InvalidOpForBools(_) => "can't do this op on bools".into_cow(), InvalidOpForFloats(_) => "can't do this op on floats".into_cow(), InvalidOpForIntUint(..) => "can't do this op on an isize and usize".into_cow(), @@ -492,6 +505,7 @@ impl ConstEvalErr { MiscBinaryOp => "bad operands for binary".into_cow(), MiscCatchAll => "unsupported constant expr".into_cow(), IndexOpFeatureGated => "the index operation on const values is unstable".into_cow(), + Math(ref err) => err.description().into_cow(), } } } @@ -536,276 +550,12 @@ impl<'tcx> EvalHint<'tcx> { } } -#[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: &TyCtxt, t: ast::IntTy) -> IntTy { - let t = if let ast::IntTy::Is = t { - tcx.sess.target.int_type - } else { - t - }; - match t { - ast::IntTy::Is => unreachable!(), - ast::IntTy::I8 => IntTy::I8, - ast::IntTy::I16 => IntTy::I16, - ast::IntTy::I32 => IntTy::I32, - ast::IntTy::I64 => IntTy::I64, - } - } -} - -impl UintTy { - pub fn from(tcx: &TyCtxt, t: ast::UintTy) -> UintTy { - let t = if let ast::UintTy::Us = t { - tcx.sess.target.uint_type - } else { - t - }; - match t { - ast::UintTy::Us => unreachable!(), - ast::UintTy::U8 => UintTy::U8, - ast::UintTy::U16 => UintTy::U16, - ast::UintTy::U32 => UintTy::U32, - ast::UintTy::U64 => UintTy::U64, - } - } -} - 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) -> 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(Int(-a)) - } -} - -pub fn const_uint_checked_neg<'a>( - a: u64, _e: &'a Expr, _opt_ety: Option) -> EvalResult { - // This always succeeds, and by definition, returns `(!a)+1`. - Ok(Uint((!a).wrapping_add(1))) -} - -fn const_uint_not(a: u64, opt_ety: Option) -> ConstVal { - let mask = match opt_ety { - Some(UintTy::U8) => u8::MAX as u64, - Some(UintTy::U16) => u16::MAX as u64, - Some(UintTy::U32) => u32::MAX as u64, - None | Some(UintTy::U64) => u64::MAX, - }; - Uint(!a & mask) -} - -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 Int AddiWithOverflow(a, b) -}} - -pub_fn_checked_op!{ const_int_checked_sub(a: i64, b: i64,.. IntTy) { - int_arith_body overflowing_sub Int SubiWithOverflow(a, b) -}} - -pub_fn_checked_op!{ const_int_checked_mul(a: i64, b: i64,.. IntTy) { - int_arith_body overflowing_mul Int MuliWithOverflow(a, b) -}} - -pub fn const_int_checked_div<'a>( - a: i64, b: i64, e: &'a Expr, opt_ety: Option) -> EvalResult { - if b == 0 { signal!(e, DivideByZero); } - let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_div); - if !oflo { Ok(Int(ret)) } else { signal!(e, DivideWithOverflow) } -} - -pub fn const_int_checked_rem<'a>( - a: i64, b: i64, e: &'a Expr, opt_ety: Option) -> EvalResult { - if b == 0 { signal!(e, ModuloByZero); } - let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_rem); - if !oflo { Ok(Int(ret)) } else { signal!(e, ModuloWithOverflow) } -} - -pub_fn_checked_op!{ const_int_checked_shl(a: i64, b: i64,.. IntTy) { - int_shift_body overflowing_shl Int ShiftLeftWithOverflow -}} - -pub_fn_checked_op!{ const_int_checked_shl_via_uint(a: i64, b: u64,.. IntTy) { - int_shift_body overflowing_shl Int ShiftLeftWithOverflow -}} - -pub_fn_checked_op!{ const_int_checked_shr(a: i64, b: i64,.. IntTy) { - int_shift_body overflowing_shr Int ShiftRightWithOverflow -}} - -pub_fn_checked_op!{ const_int_checked_shr_via_uint(a: i64, b: u64,.. IntTy) { - int_shift_body overflowing_shr Int ShiftRightWithOverflow -}} - -pub_fn_checked_op!{ const_uint_checked_add(a: u64, b: u64,.. UintTy) { - uint_arith_body overflowing_add Uint AdduWithOverflow(a, b) -}} - -pub_fn_checked_op!{ const_uint_checked_sub(a: u64, b: u64,.. UintTy) { - uint_arith_body overflowing_sub Uint SubuWithOverflow(a, b) -}} - -pub_fn_checked_op!{ const_uint_checked_mul(a: u64, b: u64,.. UintTy) { - uint_arith_body overflowing_mul Uint MuluWithOverflow(a, b) -}} - -pub fn const_uint_checked_div<'a>( - a: u64, b: u64, e: &'a Expr, opt_ety: Option) -> EvalResult { - if b == 0 { signal!(e, DivideByZero); } - let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_div); - if !oflo { Ok(Uint(ret)) } else { signal!(e, DivideWithOverflow) } -} - -pub fn const_uint_checked_rem<'a>( - a: u64, b: u64, e: &'a Expr, opt_ety: Option) -> EvalResult { - if b == 0 { signal!(e, ModuloByZero); } - let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_rem); - if !oflo { Ok(Uint(ret)) } else { signal!(e, ModuloWithOverflow) } -} - -pub_fn_checked_op!{ const_uint_checked_shl(a: u64, b: u64,.. UintTy) { - uint_shift_body overflowing_shl Uint ShiftLeftWithOverflow -}} - -pub_fn_checked_op!{ const_uint_checked_shl_via_int(a: u64, b: i64,.. UintTy) { - uint_shift_body overflowing_shl Uint ShiftLeftWithOverflow -}} - -pub_fn_checked_op!{ const_uint_checked_shr(a: u64, b: u64,.. UintTy) { - uint_shift_body overflowing_shr Uint ShiftRightWithOverflow -}} - -pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) { - uint_shift_body overflowing_shr Uint ShiftRightWithOverflow -}} - /// Evaluate a constant expression in a context where the expression isn't /// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked, /// but a few places need to evaluate constants during type-checking, like @@ -833,34 +583,57 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, tcx.expr_ty_opt(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::TyInt(t) = ty.sty { - Some(IntTy::from(tcx, t)) } else { None } - }); - let expr_uint_type = ety.and_then(|ty| { - if let ty::TyUint(t) = ty.sty { - Some(UintTy::from(tcx, t)) } else { None } - }); - let result = match e.node { hir::ExprUnary(hir::UnNeg, ref inner) => { + // unary neg literals already got their sign during creation + if let hir::ExprLit(ref lit) = inner.node { + use syntax::ast::*; + use syntax::ast::LitIntType::*; + const I8_OVERFLOW: u64 = ::std::i8::MAX as u64 + 1; + const I16_OVERFLOW: u64 = ::std::i16::MAX as u64 + 1; + const I32_OVERFLOW: u64 = ::std::i32::MAX as u64 + 1; + const I64_OVERFLOW: u64 = ::std::i64::MAX as u64 + 1; + match (&lit.node, ety.map(|t| &t.sty)) { + (&LitKind::Int(I8_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I8))) | + (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { + return Ok(Integral(I8(::std::i8::MIN))) + }, + (&LitKind::Int(I16_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I16))) | + (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { + return Ok(Integral(I16(::std::i16::MIN))) + }, + (&LitKind::Int(I32_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I32))) | + (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { + return Ok(Integral(I32(::std::i32::MIN))) + }, + (&LitKind::Int(I64_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I64))) | + (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { + return Ok(Integral(I64(::std::i64::MIN))) + }, + (&LitKind::Int(n, Unsuffixed), Some(&ty::TyInt(IntTy::Is))) | + (&LitKind::Int(n, Signed(IntTy::Is)), _) => { + match tcx.sess.target.int_type { + IntTy::I32 => if n == I32_OVERFLOW { + return Ok(Integral(Isize(Is32(::std::i32::MIN)))); + }, + IntTy::I64 => if n == I64_OVERFLOW { + return Ok(Integral(Isize(Is64(::std::i64::MIN)))); + }, + _ => unreachable!(), + } + }, + _ => {}, + } + } match try!(eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)) { Float(f) => Float(-f), - Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)), - Uint(i) => { - try!(const_uint_checked_neg(i, e, expr_uint_type)) - } + Integral(i) => Integral(math!(e, -i)), const_val => signal!(e, NegateOn(const_val)), } } hir::ExprUnary(hir::UnNot, ref inner) => { match try!(eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)) { - Int(i) => Int(!i), - Uint(i) => const_uint_not(i, expr_uint_type), + Integral(i) => Integral(math!(e, !i)), Bool(b) => Bool(!b), const_val => signal!(e, NotOn(const_val)), } @@ -870,6 +643,10 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, hir::BiShl | hir::BiShr => ty_hint.checked_or(tcx.types.usize), _ => ty_hint }; + // technically, if we don't have type hints, but integral eval + // gives us a type through a type-suffix, cast or const def type + // we need to re-eval the other value of the BinOp if it was + // not inferred match (try!(eval_const_expr_partial(tcx, &a, ty_hint, fn_args)), try!(eval_const_expr_partial(tcx, &b, b_ty, fn_args))) { (Float(a), Float(b)) => { @@ -888,63 +665,28 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, _ => signal!(e, InvalidOpForFloats(op.node)), } } - (Int(a), Int(b)) => { + (Integral(a), Integral(b)) => { + use std::cmp::Ordering::*; match op.node { - hir::BiAdd => try!(const_int_checked_add(a,b,e,expr_int_type)), - hir::BiSub => try!(const_int_checked_sub(a,b,e,expr_int_type)), - hir::BiMul => try!(const_int_checked_mul(a,b,e,expr_int_type)), - hir::BiDiv => try!(const_int_checked_div(a,b,e,expr_int_type)), - hir::BiRem => try!(const_int_checked_rem(a,b,e,expr_int_type)), - hir::BiBitAnd => Int(a & b), - hir::BiBitOr => Int(a | b), - hir::BiBitXor => Int(a ^ b), - hir::BiShl => try!(const_int_checked_shl(a,b,e,expr_int_type)), - hir::BiShr => try!(const_int_checked_shr(a,b,e,expr_int_type)), - hir::BiEq => Bool(a == b), - hir::BiLt => Bool(a < b), - hir::BiLe => Bool(a <= b), - hir::BiNe => Bool(a != b), - hir::BiGe => Bool(a >= b), - hir::BiGt => Bool(a > b), + hir::BiAdd => Integral(math!(e, a + b)), + hir::BiSub => Integral(math!(e, a - b)), + hir::BiMul => Integral(math!(e, a * b)), + hir::BiDiv => Integral(math!(e, a / b)), + hir::BiRem => Integral(math!(e, a % b)), + hir::BiBitAnd => Integral(math!(e, a & b)), + hir::BiBitOr => Integral(math!(e, a | b)), + hir::BiBitXor => Integral(math!(e, a ^ b)), + hir::BiShl => Integral(math!(e, a << b)), + hir::BiShr => Integral(math!(e, a >> b)), + hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal), + hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less), + hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater), + hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), + hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), + hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), _ => signal!(e, InvalidOpForInts(op.node)), } } - (Uint(a), Uint(b)) => { - match op.node { - hir::BiAdd => try!(const_uint_checked_add(a,b,e,expr_uint_type)), - hir::BiSub => try!(const_uint_checked_sub(a,b,e,expr_uint_type)), - hir::BiMul => try!(const_uint_checked_mul(a,b,e,expr_uint_type)), - hir::BiDiv => try!(const_uint_checked_div(a,b,e,expr_uint_type)), - hir::BiRem => try!(const_uint_checked_rem(a,b,e,expr_uint_type)), - hir::BiBitAnd => Uint(a & b), - hir::BiBitOr => Uint(a | b), - hir::BiBitXor => Uint(a ^ b), - hir::BiShl => try!(const_uint_checked_shl(a,b,e,expr_uint_type)), - hir::BiShr => try!(const_uint_checked_shr(a,b,e,expr_uint_type)), - hir::BiEq => Bool(a == b), - hir::BiLt => Bool(a < b), - hir::BiLe => Bool(a <= b), - hir::BiNe => Bool(a != b), - hir::BiGe => Bool(a >= b), - hir::BiGt => Bool(a > b), - _ => signal!(e, InvalidOpForUInts(op.node)), - } - } - // shifts can have any integral type as their rhs - (Int(a), Uint(b)) => { - match op.node { - hir::BiShl => try!(const_int_checked_shl_via_uint(a,b,e,expr_int_type)), - hir::BiShr => try!(const_int_checked_shr_via_uint(a,b,e,expr_int_type)), - _ => signal!(e, InvalidOpForIntUint(op.node)), - } - } - (Uint(a), Int(b)) => { - match op.node { - hir::BiShl => try!(const_uint_checked_shl_via_int(a,b,e,expr_uint_type)), - hir::BiShr => try!(const_uint_checked_shr_via_int(a,b,e,expr_uint_type)), - _ => signal!(e, InvalidOpForUintInt(op.node)), - } - } (Bool(a), Bool(b)) => { Bool(match op.node { hir::BiAnd => a && b, @@ -962,7 +704,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, } } hir::ExprCast(ref base, ref target_ty) => { - let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &target_ty)) + let ety = ast_ty_to_prim_ty(tcx, &target_ty).or_else(|| ety) .unwrap_or_else(|| { tcx.sess.span_fatal(target_ty.span, "target type not found for const cast") @@ -971,9 +713,6 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, let base_hint = if let ExprTypeChecked = ty_hint { ExprTypeChecked } else { - // 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. match tcx.expr_ty_opt(&base) { Some(t) => UncheckedExprHint(t), None => ty_hint @@ -1103,10 +842,28 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, let mut call_args = NodeMap(); for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) { + let arg_hint = match ty_hint { + ExprTypeChecked => ExprTypeChecked, + UncheckedExprNoHint | UncheckedExprHint(_) => { + if let Some(hint) = tcx.ast_ty_to_ty_cache.borrow().get(&arg.ty.id) { + let mut new_ty_hint = UncheckedExprHint(hint); + for t in hint.walk() { + if let ty::TypeVariants::TyParam(_) = t.sty { + // found a generic argument, but we are in typeck + new_ty_hint = UncheckedExprNoHint; + break; + } + } + new_ty_hint + } else { + UncheckedExprNoHint + } + }, + }; let arg_val = try!(eval_const_expr_partial( tcx, arg_expr, - sub_ty_hint, + arg_hint, fn_args )); debug!("const call arg: {:?}", arg); @@ -1116,7 +873,10 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, debug!("const call({:?})", call_args); try!(eval_const_expr_partial(tcx, &result, ty_hint, Some(&call_args))) }, - hir::ExprLit(ref lit) => lit_to_const(tcx.sess, e.span, &lit, ety), + hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ety, lit.span) { + Ok(val) => val, + Err(err) => signal!(e, Math(err)), + }, hir::ExprBlock(ref block) => { match block.expr { Some(ref expr) => try!(eval_const_expr_partial(tcx, &expr, ty_hint, fn_args)), @@ -1134,14 +894,15 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, let arr = try!(eval_const_expr_partial(tcx, arr, arr_hint, fn_args)); let idx_hint = ty_hint.checked_or(tcx.types.usize); let idx = match try!(eval_const_expr_partial(tcx, idx, idx_hint, fn_args)) { - Int(i) if i >= 0 => i as u64, - Int(_) => signal!(idx, IndexNegative), - Uint(i) => i, + Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), + Integral(_) => unreachable!(), _ => signal!(idx, IndexNotInt), }; + assert_eq!(idx as usize as u64, idx); match arr { Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds), - Array(v, _) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node { + Array(v, n) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node { + assert_eq!(n as usize as u64, n); try!(eval_const_expr_partial(tcx, &v[idx as usize], ty_hint, fn_args)) } else { unreachable!() @@ -1155,13 +916,13 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, fn_args, )), - ByteStr(ref data) if idx as usize >= data.len() - => signal!(e, IndexOutOfBounds), - ByteStr(data) => Uint(data[idx as usize] as u64), + ByteStr(ref data) if idx >= data.len() as u64 => signal!(e, IndexOutOfBounds), + ByteStr(data) => { + Integral(U8(data[idx as usize])) + }, - Str(ref s) if idx as usize >= s.len() - => signal!(e, IndexOutOfBounds), - Str(_) => unimplemented!(), // there's no const_char type + Str(ref s) if idx as usize >= s.len() => signal!(e, IndexOutOfBounds), + Str(_) => unimplemented!(), // FIXME: return a const char _ => signal!(e, IndexedNonVec), } } @@ -1171,9 +932,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, Repeat( e.id, match try!(eval_const_expr_partial(tcx, &n, len_hint, fn_args)) { - Int(i) if i >= 0 => i as u64, - Int(_) => signal!(e, RepeatCountNotNatural), - Uint(i) => i, + Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), + Integral(_) => signal!(e, RepeatCountNotNatural), _ => signal!(e, RepeatCountNotInt), }, ) @@ -1184,7 +944,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, if let Tuple(tup_id) = c { if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node { if index.node < fields.len() { - return eval_const_expr_partial(tcx, &fields[index.node], base_hint, fn_args) + return eval_const_expr_partial(tcx, &fields[index.node], ty_hint, fn_args) } else { signal!(e, TupleIndexOutOfBounds); } @@ -1205,7 +965,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, // if the idents are compared run-pass/issue-19244 fails if let Some(f) = fields.iter().find(|f| f.name.node == field_name.node) { - return eval_const_expr_partial(tcx, &f.expr, base_hint, fn_args) + return eval_const_expr_partial(tcx, &f.expr, ty_hint, fn_args) } else { signal!(e, MissingStructField); } @@ -1289,100 +1049,155 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, } } -fn cast_const<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstVal, ty: Ty) -> CastResult { - macro_rules! convert_val { - ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => { - match val { - Bool(b) => Ok($const_type(b as u64 as $intermediate_ty as $target_ty)), - Uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)), - Int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)), - Float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)), - _ => Err(ErrKind::CannotCastTo(stringify!($const_type))), - } - } +fn cast_const_int<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstInt, ty: ty::Ty) -> CastResult { + let v = val.to_u64_unchecked(); + match ty.sty { + ty::TyBool if v == 0 => Ok(Bool(false)), + ty::TyBool if v == 1 => Ok(Bool(true)), + ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i8))), + ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i16))), + ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i32))), + ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i64))), + ty::TyInt(ast::IntTy::Is) => { + Ok(Integral(Isize(try!(ConstIsize::new(v as i64, tcx.sess.target.int_type))))) + }, + ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))), + ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))), + ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))), + ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v as u64))), + ty::TyUint(ast::UintTy::Us) => { + Ok(Integral(Usize(try!(ConstUsize::new(v, tcx.sess.target.uint_type))))) + }, + ty::TyFloat(ast::FloatTy::F64) if val.is_negative() => { + // FIXME: this could probably be prettier + // there's no easy way to turn an `Infer` into a f64 + let val = try!((-val).map_err(Math)); + let val = val.to_u64().unwrap() as f64; + let val = -val; + Ok(Float(val)) + }, + ty::TyFloat(ast::FloatTy::F64) => Ok(Float(val.to_u64().unwrap() as f64)), + ty::TyFloat(ast::FloatTy::F32) if val.is_negative() => { + let val = try!((-val).map_err(Math)); + let val = val.to_u64().unwrap() as f32; + let val = -val; + Ok(Float(val as f64)) + }, + ty::TyFloat(ast::FloatTy::F32) => Ok(Float(val.to_u64().unwrap() as f32 as f64)), + _ => Err(CannotCast), } +} - // 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::TyInt(ast::IntTy::Is), ast::IntTy::I32, _) => return convert_val!(i32, Int, i64), - (&ty::TyInt(ast::IntTy::Is), ast::IntTy::I64, _) => return convert_val!(i64, Int, i64), - (&ty::TyInt(ast::IntTy::Is), _, _) => panic!("unexpected target.int_type"), - - (&ty::TyUint(ast::UintTy::Us), _, ast::UintTy::U32) => return convert_val!(u32, Uint, u64), - (&ty::TyUint(ast::UintTy::Us), _, ast::UintTy::U64) => return convert_val!(u64, Uint, u64), - (&ty::TyUint(ast::UintTy::Us), _, _) => panic!("unexpected target.uint_type"), - - _ => {} +fn cast_const_float<'tcx>(tcx: &ty::ctxt<'tcx>, f: f64, ty: ty::Ty) -> CastResult { + match ty.sty { + ty::TyInt(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty), + ty::TyInt(_) => cast_const_int(tcx, InferSigned(f as i64), ty), + ty::TyUint(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty), + ty::TyFloat(ast::FloatTy::F64) => Ok(Float(f)), + ty::TyFloat(ast::FloatTy::F32) => Ok(Float(f as f32 as f64)), + _ => Err(CannotCast), } +} - match ty.sty { - ty::TyInt(ast::IntTy::Is) => unreachable!(), - ty::TyUint(ast::UintTy::Us) => unreachable!(), - - ty::TyInt(ast::IntTy::I8) => convert_val!(i8, Int, i64), - ty::TyInt(ast::IntTy::I16) => convert_val!(i16, Int, i64), - ty::TyInt(ast::IntTy::I32) => convert_val!(i32, Int, i64), - ty::TyInt(ast::IntTy::I64) => convert_val!(i64, Int, i64), - - ty::TyUint(ast::UintTy::U8) => convert_val!(u8, Uint, u64), - ty::TyUint(ast::UintTy::U16) => convert_val!(u16, Uint, u64), - ty::TyUint(ast::UintTy::U32) => convert_val!(u32, Uint, u64), - ty::TyUint(ast::UintTy::U64) => convert_val!(u64, Uint, u64), - - ty::TyFloat(ast::FloatTy::F32) => convert_val!(f32, Float, f64), - ty::TyFloat(ast::FloatTy::F64) => convert_val!(f64, Float, f64), - _ => Err(ErrKind::CannotCast), +fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: ConstVal, ty: ty::Ty) -> CastResult { + match val { + Integral(i) => cast_const_int(tcx, i, ty), + Bool(b) => cast_const_int(tcx, Infer(b as u64), ty), + Float(f) => cast_const_float(tcx, f, ty), + Char(c) => cast_const_int(tcx, Infer(c as u64), ty), + _ => Err(CannotCast), } } -fn lit_to_const(sess: &Session, span: Span, lit: &ast::Lit, ty_hint: Option) -> ConstVal { - match lit.node { - ast::LitKind::Str(ref s, _) => Str((*s).clone()), - ast::LitKind::ByteStr(ref data) => { - ByteStr(data.clone()) - } - ast::LitKind::Byte(n) => Uint(n as u64), - ast::LitKind::Char(n) => Uint(n as u64), - ast::LitKind::Int(n, ast::LitIntType::Signed(_)) => Int(n as i64), - ast::LitKind::Int(n, ast::LitIntType::Unsuffixed) => { - match ty_hint.map(|ty| &ty.sty) { - Some(&ty::TyUint(_)) => Uint(n), - _ => Int(n as i64) +fn lit_to_const<'tcx>(lit: &ast::LitKind, + tcx: &ty::ctxt<'tcx>, + ty_hint: Option>, + span: Span, + ) -> Result { + use syntax::ast::*; + use syntax::ast::LitIntType::*; + const I8MAX: u64 = ::std::i8::MAX as u64; + const I16MAX: u64 = ::std::i16::MAX as u64; + const I32MAX: u64 = ::std::i32::MAX as u64; + const I64MAX: u64 = ::std::i64::MAX as u64; + const U8MAX: u64 = ::std::u8::MAX as u64; + const U16MAX: u64 = ::std::u16::MAX as u64; + const U32MAX: u64 = ::std::u32::MAX as u64; + const U64MAX: u64 = ::std::u64::MAX as u64; + match *lit { + LitKind::Str(ref s, _) => Ok(Str((*s).clone())), + LitKind::ByteStr(ref data) => Ok(ByteStr(data.clone())), + LitKind::Byte(n) => Ok(Integral(U8(n))), + LitKind::Int(n @ 0...I8MAX, Signed(IntTy::I8)) => Ok(Integral(I8(n as i8))), + LitKind::Int(n @ 0...I16MAX, Signed(IntTy::I16)) => Ok(Integral(I16(n as i16))), + LitKind::Int(n @ 0...I32MAX, Signed(IntTy::I32)) => Ok(Integral(I32(n as i32))), + LitKind::Int(n @ 0...I64MAX, Signed(IntTy::I64)) => Ok(Integral(I64(n as i64))), + LitKind::Int(n, Signed(IntTy::Is)) => { + Ok(Integral(Isize(try!(ConstIsize::new(n as i64, tcx.sess.target.int_type))))) + }, + + LitKind::Int(_, Signed(ty)) => Err(ConstMathErr::LitOutOfRange(ty)), + + LitKind::Int(n, Unsuffixed) => { + match ty_hint.map(|t| &t.sty) { + Some(&ty::TyInt(ity)) => { + lit_to_const(&LitKind::Int(n, Signed(ity)), tcx, ty_hint, span) + }, + Some(&ty::TyUint(uty)) => { + lit_to_const(&LitKind::Int(n, Unsigned(uty)), tcx, ty_hint, span) + }, + None => Ok(Integral(Infer(n))), + Some(&ty::TyEnum(ref adt, _)) => { + let hints = tcx.lookup_repr_hints(adt.did); + let int_ty = tcx.enum_repr_type(hints.iter().next()); + lit_to_const(lit, tcx, Some(int_ty.to_ty(tcx)), span) + }, + Some(ty_hint) => panic!("bad ty_hint: {:?}, {:?}", ty_hint, lit), } - } - ast::LitKind::Int(n, ast::LitIntType::Unsigned(_)) => Uint(n), - ast::LitKind::Float(ref n, _) | - ast::LitKind::FloatUnsuffixed(ref n) => { + }, + LitKind::Int(n @ 0...U8MAX, Unsigned(UintTy::U8)) => Ok(Integral(U8(n as u8))), + LitKind::Int(n @ 0...U16MAX, Unsigned(UintTy::U16)) => Ok(Integral(U16(n as u16))), + LitKind::Int(n @ 0...U32MAX, Unsigned(UintTy::U32)) => Ok(Integral(U32(n as u32))), + LitKind::Int(n @ 0...U64MAX, Unsigned(UintTy::U64)) => Ok(Integral(U64(n as u64))), + + LitKind::Int(n, Unsigned(UintTy::Us)) => { + Ok(Integral(Usize(try!(ConstUsize::new(n as u64, tcx.sess.target.uint_type))))) + }, + LitKind::Int(_, Unsigned(ty)) => Err(ConstMathErr::ULitOutOfRange(ty)), + + LitKind::Float(ref n, _) | + LitKind::FloatUnsuffixed(ref n) => { if let Ok(x) = n.parse::() { - Float(x) + Ok(Float(x)) } else { // FIXME(#31407) this is only necessary because float parsing is buggy - sess.span_bug(span, "could not evaluate float literal (see issue #31407)"); + tcx.sess.span_bug(span, "could not evaluate float literal (see issue #31407)"); } } - ast::LitKind::Bool(b) => Bool(b) + LitKind::Bool(b) => Ok(Bool(b)), + LitKind::Char(c) => Ok(Char(c)), } } pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option { - Some(match (a, b) { - (&Int(a), &Int(b)) => a.cmp(&b), - (&Uint(a), &Uint(b)) => a.cmp(&b), + match (a, b) { + (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(), (&Float(a), &Float(b)) => { // This is pretty bad but it is the existing behavior. - if a == b { + Some(if a == b { Ordering::Equal } else if a < b { Ordering::Less } else { Ordering::Greater - } + }) } - (&Str(ref a), &Str(ref b)) => a.cmp(b), - (&Bool(a), &Bool(b)) => a.cmp(&b), - (&ByteStr(ref a), &ByteStr(ref b)) => a.cmp(b), - _ => return None - }) + (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)), + (&Bool(a), &Bool(b)) => Some(a.cmp(&b)), + (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)), + (&Char(a), &Char(ref b)) => Some(a.cmp(b)), + _ => None, + } } pub fn compare_lit_exprs<'tcx>(tcx: &TyCtxt<'tcx>, diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 2e38080bfb070..c98ae2cceabaa 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -50,6 +50,8 @@ use syntax::attr::{self, AttrMetaMethods}; use syntax::codemap::{DUMMY_SP, Span}; use syntax::parse::token::InternedString; +use rustc_const_eval::ConstInt; + use rustc_front::hir; use rustc_front::hir::{ItemImpl, ItemTrait, PatKind}; use rustc_front::intravisit::Visitor; @@ -100,8 +102,7 @@ mod ivar; mod structural_impls; mod sty; -pub type Disr = u64; -pub const INITIAL_DISCRIMINANT_VALUE: Disr = 0; +pub type Disr = ConstInt; // Data types diff --git a/src/librustc/middle/ty/util.rs b/src/librustc/middle/ty/util.rs index 2b83aaccdc46b..c91441a3f8a4b 100644 --- a/src/librustc/middle/ty/util.rs +++ b/src/librustc/middle/ty/util.rs @@ -21,7 +21,8 @@ use middle::traits; use middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable}; use middle::ty::{Disr, ParameterEnvironment}; use middle::ty::TypeVariants::*; -use util::num::ToPrimitive; + +use rustc_const_eval::{ConstInt, ConstIsize, ConstUsize}; use std::cmp; use std::hash::{Hash, SipHasher, Hasher}; @@ -34,11 +35,9 @@ use rustc_front::hir; pub trait IntTypeExt { fn to_ty<'tcx>(&self, cx: &TyCtxt<'tcx>) -> Ty<'tcx>; - fn i64_to_disr(&self, val: i64) -> Option; - fn u64_to_disr(&self, val: u64) -> Option; fn disr_incr(&self, val: Disr) -> Option; - fn disr_string(&self, val: Disr) -> String; - fn disr_wrap_incr(&self, val: Option) -> Disr; + fn assert_ty_matches(&self, val: Disr); + fn initial_discriminant(&self, tcx: &TyCtxt) -> Disr; } impl IntTypeExt for attr::IntType { @@ -57,98 +56,48 @@ impl IntTypeExt for attr::IntType { } } - fn i64_to_disr(&self, val: i64) -> Option { + fn initial_discriminant(&self, tcx: &TyCtxt) -> Disr { match *self { - SignedInt(ast::IntTy::I8) => val.to_i8() .map(|v| v as Disr), - SignedInt(ast::IntTy::I16) => val.to_i16() .map(|v| v as Disr), - SignedInt(ast::IntTy::I32) => val.to_i32() .map(|v| v as Disr), - SignedInt(ast::IntTy::I64) => val.to_i64() .map(|v| v as Disr), - UnsignedInt(ast::UintTy::U8) => val.to_u8() .map(|v| v as Disr), - UnsignedInt(ast::UintTy::U16) => val.to_u16() .map(|v| v as Disr), - UnsignedInt(ast::UintTy::U32) => val.to_u32() .map(|v| v as Disr), - UnsignedInt(ast::UintTy::U64) => val.to_u64() .map(|v| v as Disr), - - UnsignedInt(ast::UintTy::Us) | - SignedInt(ast::IntTy::Is) => unreachable!(), + SignedInt(ast::IntTy::I8) => ConstInt::I8(0), + SignedInt(ast::IntTy::I16) => ConstInt::I16(0), + SignedInt(ast::IntTy::I32) => ConstInt::I32(0), + SignedInt(ast::IntTy::I64) => ConstInt::I64(0), + SignedInt(ast::IntTy::Is) => match tcx.sess.target.int_type { + ast::IntTy::I32 => ConstInt::Isize(ConstIsize::Is32(0)), + ast::IntTy::I64 => ConstInt::Isize(ConstIsize::Is64(0)), + _ => unreachable!(), + }, + UnsignedInt(ast::UintTy::U8) => ConstInt::U8(0), + UnsignedInt(ast::UintTy::U16) => ConstInt::U16(0), + UnsignedInt(ast::UintTy::U32) => ConstInt::U32(0), + UnsignedInt(ast::UintTy::U64) => ConstInt::U64(0), + UnsignedInt(ast::UintTy::Us) => match tcx.sess.target.uint_type { + ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32(0)), + ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64(0)), + _ => unreachable!(), + }, } } - fn u64_to_disr(&self, val: u64) -> Option { - match *self { - SignedInt(ast::IntTy::I8) => val.to_i8() .map(|v| v as Disr), - SignedInt(ast::IntTy::I16) => val.to_i16() .map(|v| v as Disr), - SignedInt(ast::IntTy::I32) => val.to_i32() .map(|v| v as Disr), - SignedInt(ast::IntTy::I64) => val.to_i64() .map(|v| v as Disr), - UnsignedInt(ast::UintTy::U8) => val.to_u8() .map(|v| v as Disr), - UnsignedInt(ast::UintTy::U16) => val.to_u16() .map(|v| v as Disr), - UnsignedInt(ast::UintTy::U32) => val.to_u32() .map(|v| v as Disr), - UnsignedInt(ast::UintTy::U64) => val.to_u64() .map(|v| v as Disr), - - UnsignedInt(ast::UintTy::Us) | - SignedInt(ast::IntTy::Is) => unreachable!(), + fn assert_ty_matches(&self, val: Disr) { + match (*self, val) { + (SignedInt(ast::IntTy::I8), ConstInt::I8(_)) => {}, + (SignedInt(ast::IntTy::I16), ConstInt::I16(_)) => {}, + (SignedInt(ast::IntTy::I32), ConstInt::I32(_)) => {}, + (SignedInt(ast::IntTy::I64), ConstInt::I64(_)) => {}, + (SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) => {}, + (UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) => {}, + (UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) => {}, + (UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) => {}, + (UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) => {}, + (UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => {}, + _ => panic!("disr type mismatch: {:?} vs {:?}", self, val), } } fn disr_incr(&self, val: Disr) -> Option { - 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::IntTy::I8) => add1!((val as i64).to_i8()), - SignedInt(ast::IntTy::I16) => add1!((val as i64).to_i16()), - SignedInt(ast::IntTy::I32) => add1!((val as i64).to_i32()), - SignedInt(ast::IntTy::I64) => add1!(Some(val as i64)), - - UnsignedInt(ast::UintTy::U8) => add1!(val.to_u8()), - UnsignedInt(ast::UintTy::U16) => add1!(val.to_u16()), - UnsignedInt(ast::UintTy::U32) => add1!(val.to_u32()), - UnsignedInt(ast::UintTy::U64) => add1!(Some(val)), - - UnsignedInt(ast::UintTy::Us) | - SignedInt(ast::IntTy::Is) => 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::IntTy::I8) => format!("{}", val as i8 ), - SignedInt(ast::IntTy::I16) => format!("{}", val as i16), - SignedInt(ast::IntTy::I32) => format!("{}", val as i32), - SignedInt(ast::IntTy::I64) => format!("{}", val as i64), - UnsignedInt(ast::UintTy::U8) => format!("{}", val as u8 ), - UnsignedInt(ast::UintTy::U16) => format!("{}", val as u16), - UnsignedInt(ast::UintTy::U32) => format!("{}", val as u32), - UnsignedInt(ast::UintTy::U64) => format!("{}", val as u64), - - UnsignedInt(ast::UintTy::Us) | - SignedInt(ast::IntTy::Is) => unreachable!(), - } - } - - fn disr_wrap_incr(&self, val: Option) -> 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::IntTy::I8) => add1!(val as i8 ), - SignedInt(ast::IntTy::I16) => add1!(val as i16), - SignedInt(ast::IntTy::I32) => add1!(val as i32), - SignedInt(ast::IntTy::I64) => add1!(val as i64), - UnsignedInt(ast::UintTy::U8) => add1!(val as u8 ), - UnsignedInt(ast::UintTy::U16) => add1!(val as u16), - UnsignedInt(ast::UintTy::U32) => add1!(val as u32), - UnsignedInt(ast::UintTy::U64) => add1!(val as u64), - - UnsignedInt(ast::UintTy::Us) | - SignedInt(ast::IntTy::Is) => unreachable!(), - } + self.assert_ty_matches(val); + (val + ConstInt::Infer(1)).ok() } } @@ -266,13 +215,11 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// 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(&self, opt_hint: Option<&attr::ReprAttr>) - -> (attr::IntType, Ty<'tcx>) { - let repr_type = match opt_hint { + /// Returns the IntType representation. + /// This used to ensure `int_ty` doesn't contain `usize` and `isize` + /// by converting them to their actual types. That doesn't happen anymore. + pub fn enum_repr_type(&self, opt_hint: Option<&attr::ReprAttr>) -> attr::IntType { + match opt_hint { // Feed in the given type Some(&attr::ReprInt(_, int_t)) => int_t, // ... but provide sensible default if none provided @@ -280,18 +227,7 @@ impl<'tcx> TyCtxt<'tcx> { // NB. Historically `fn enum_variants` generate i64 here, while // rustc_typeck::check would generate isize. _ => SignedInt(ast::IntTy::Is), - }; - - let repr_type_ty = repr_type.to_ty(self); - let repr_type = match repr_type { - SignedInt(ast::IntTy::Is) => - SignedInt(self.sess.target.int_type), - UnsignedInt(ast::UintTy::Us) => - UnsignedInt(self.sess.target.uint_type), - other => other - }; - - (repr_type, repr_type_ty) + } } /// Returns the deeply last field of nested structures, or the same type, @@ -335,15 +271,16 @@ impl<'tcx> TyCtxt<'tcx> { pub fn eval_repeat_count(&self, count_expr: &hir::Expr) -> usize { let hint = UncheckedExprHint(self.types.usize); match const_eval::eval_const_expr_partial(self, count_expr, hint, None) { - Ok(val) => { - let found = match val { - ConstVal::Uint(count) => return count as usize, - ConstVal::Int(count) if count >= 0 => return count as usize, - const_val => const_val.description(), - }; + Ok(ConstVal::Integral(ConstInt::Usize(count))) => { + let val = count.as_u64(self.sess.target.uint_type); + assert_eq!(val as usize as u64, val); + val as usize + }, + Ok(const_val) => { span_err!(self.sess, count_expr.span, E0306, - "expected positive integer for repeat count, found {}", - found); + "expected positive integer for repeat count, found {}", + const_val.description()); + 0 } Err(err) => { let err_msg = match count_expr.node { @@ -360,9 +297,9 @@ impl<'tcx> TyCtxt<'tcx> { }; span_err!(self.sess, count_expr.span, E0307, "expected constant integer for repeat count, {}", err_msg); + 0 } } - 0 } /// Given a set of predicates that apply to an object type, returns diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 4556611df594b..15f240755adda 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -10,6 +10,7 @@ use graphviz::IntoCow; use middle::const_eval::ConstVal; +use rustc_const_eval::{ConstUsize, ConstInt}; use middle::def_id::DefId; use middle::subst::Substs; use middle::ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty}; @@ -851,13 +852,12 @@ pub struct Constant<'tcx> { pub struct TypedConstVal<'tcx> { pub ty: Ty<'tcx>, pub span: Span, - pub value: ConstVal + pub value: ConstUsize, } impl<'tcx> Debug for TypedConstVal<'tcx> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - try!(write!(fmt, "const ")); - fmt_const_val(fmt, &self.value) + write!(fmt, "const {}", ConstInt::Usize(self.value)) } } @@ -897,8 +897,7 @@ fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { use middle::const_eval::ConstVal::*; match *const_val { Float(f) => write!(fmt, "{:?}", f), - Int(n) => write!(fmt, "{:?}", n), - Uint(n) => write!(fmt, "{:?}", n), + Integral(n) => write!(fmt, "{}", n), Str(ref s) => write!(fmt, "{:?}", s), ByteStr(ref bytes) => { let escaped: String = bytes @@ -911,6 +910,8 @@ fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { Function(def_id) => write!(fmt, "{}", item_path_str(def_id)), Struct(node_id) | Tuple(node_id) | Array(node_id, _) | Repeat(node_id, _) => write!(fmt, "{}", node_to_string(node_id)), + Char(c) => write!(fmt, "{:?}", c), + Dummy => unreachable!(), } } diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 26f6db4aa4fc1..b6b2694a7cbe1 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -14,7 +14,6 @@ */ use mir::repr::*; -use middle::const_eval::ConstVal; use middle::subst::{Subst, Substs}; use middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_front::hir; @@ -144,12 +143,10 @@ impl<'tcx> Mir<'tcx> { match *rvalue { Rvalue::Use(ref operand) => Some(self.operand_ty(tcx, operand)), Rvalue::Repeat(ref operand, ref count) => { - if let ConstVal::Uint(u) = count.value { - let op_ty = self.operand_ty(tcx, operand); - Some(tcx.mk_array(op_ty, u as usize)) - } else { - None - } + let op_ty = self.operand_ty(tcx, operand); + let count = count.value.as_u64(tcx.sess.target.uint_type); + assert_eq!(count as usize as u64, count); + Some(tcx.mk_array(op_ty, count as usize)) } Rvalue::Ref(reg, bk, ref lv) => { let lv_ty = self.lvalue_ty(tcx, lv).to_ty(tcx); diff --git a/src/librustc_const_eval/err.rs b/src/librustc_const_eval/err.rs new file mode 100644 index 0000000000000..126b3824efec6 --- /dev/null +++ b/src/librustc_const_eval/err.rs @@ -0,0 +1,85 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax::ast; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ConstMathErr { + NotInRange, + CmpBetweenUnequalTypes, + UnequalTypes(Op), + Overflow(Op), + ShiftNegative, + DivisionByZero, + RemainderByZero, + UnsignedNegation, + ULitOutOfRange(ast::UintTy), + LitOutOfRange(ast::IntTy), +} +pub use self::ConstMathErr::*; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Op { + Add, + Sub, + Mul, + Div, + Rem, + Shr, + Shl, + Neg, + BitAnd, + BitOr, + BitXor, +} + +impl ConstMathErr { + pub fn description(&self) -> &'static str { + use self::Op::*; + match *self { + NotInRange => "inferred value out of range", + CmpBetweenUnequalTypes => "compared two integrals of different types", + UnequalTypes(Add) => "tried to add two integrals of different types", + UnequalTypes(Sub) => "tried to subtract two integrals of different types", + UnequalTypes(Mul) => "tried to multiply two integrals of different types", + UnequalTypes(Div) => "tried to divide two integrals of different types", + UnequalTypes(Rem) => { + "tried to calculate the remainder of two integrals of different types" + }, + UnequalTypes(BitAnd) => "tried to bitand two integrals of different types", + UnequalTypes(BitOr) => "tried to bitor two integrals of different types", + UnequalTypes(BitXor) => "tried to xor two integrals of different types", + UnequalTypes(_) => unreachable!(), + Overflow(Add) => "attempted to add with overflow", + Overflow(Sub) => "attempted to subtract with overflow", + Overflow(Mul) => "attempted to multiply with overflow", + Overflow(Div) => "attempted to divide with overflow", + Overflow(Rem) => "attempted to calculate the remainder with overflow", + Overflow(Neg) => "attempted to negate with overflow", + Overflow(Shr) => "attempted to shift right with overflow", + Overflow(Shl) => "attempted to shift left with overflow", + Overflow(_) => unreachable!(), + ShiftNegative => "attempted to shift by a negative amount", + DivisionByZero => "attempted to divide by zero", + RemainderByZero => "attempted to calculate the remainder with a divisor of zero", + UnsignedNegation => "unary negation of unsigned integer", + ULitOutOfRange(ast::UintTy::U8) => "literal out of range for u8", + ULitOutOfRange(ast::UintTy::U16) => "literal out of range for u16", + ULitOutOfRange(ast::UintTy::U32) => "literal out of range for u32", + ULitOutOfRange(ast::UintTy::U64) => "literal out of range for u64", + ULitOutOfRange(ast::UintTy::Us) => "literal out of range for usize", + LitOutOfRange(ast::IntTy::I8) => "literal out of range for i8", + LitOutOfRange(ast::IntTy::I16) => "literal out of range for i16", + LitOutOfRange(ast::IntTy::I32) => "literal out of range for i32", + LitOutOfRange(ast::IntTy::I64) => "literal out of range for i64", + LitOutOfRange(ast::IntTy::Is) => "literal out of range for isize", + } + } +} diff --git a/src/librustc_const_eval/int.rs b/src/librustc_const_eval/int.rs new file mode 100644 index 0000000000000..896d4a9b7d5e2 --- /dev/null +++ b/src/librustc_const_eval/int.rs @@ -0,0 +1,551 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cmp::Ordering; + +use super::is::*; +use super::us::*; +use super::err::*; + +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] +pub enum ConstInt { + I8(i8), + I16(i16), + I32(i32), + I64(i64), + Isize(ConstIsize), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + Usize(ConstUsize), + Infer(u64), + InferSigned(i64), +} +pub use self::ConstInt::*; + + +macro_rules! bounds { + ($($t:ident $min:ident $max:ident)*) => { + mod as_u64 { + $( + #[allow(dead_code)] + pub const $min: u64 = ::std::$t::MIN as u64; + #[allow(dead_code)] + pub const $max: u64 = ::std::$t::MAX as u64; + )* + } + mod as_i64 { + $( + #[allow(dead_code)] + pub const $min: i64 = ::std::$t::MIN as i64; + #[allow(dead_code)] + pub const $max: i64 = ::std::$t::MAX as i64; + )* + } + } +} + +bounds!{ + i8 I8MIN I8MAX i16 I16MIN I16MAX i32 I32MIN I32MAX i64 I64MIN I64MAX isize IMIN IMAX + u8 U8MIN U8MAX u16 U16MIN U16MAX u32 U32MIN U32MAX u64 U64MIN U64MAX usize UMIN UMAX +} + +impl ConstInt { + /// If either value is `Infer` or `InferSigned`, try to turn the value into the type of + /// the other value. If both values have no type, don't do anything + pub fn infer(self, other: Self) -> Result<(Self, Self), ConstMathErr> { + let inferred = match (self, other) { + (InferSigned(_), InferSigned(_)) + | (Infer(_), Infer(_)) => self, // no inference possible + // kindof wrong, you could have had values > I64MAX during computation of a + (Infer(a @ 0...as_u64::I64MAX), InferSigned(_)) => InferSigned(a as i64), + (Infer(_), InferSigned(_)) => return Err(ConstMathErr::NotInRange), + (_, InferSigned(_)) + | (_, Infer(_)) => return other.infer(self).map(|(b, a)| (a, b)), + + (Infer(a @ 0...as_u64::I8MAX), I8(_)) => I8(a as i8), + (Infer(a @ 0...as_u64::I16MAX), I16(_)) => I16(a as i16), + (Infer(a @ 0...as_u64::I32MAX), I32(_)) => I32(a as i32), + (Infer(a @ 0...as_u64::I64MAX), I64(_)) => I64(a as i64), + (Infer(a @ 0...as_u64::I32MAX), Isize(Is32(_))) => Isize(Is32(a as i32)), + (Infer(a @ 0...as_u64::I64MAX), Isize(Is64(_))) => Isize(Is64(a as i64)), + (Infer(a @ 0...as_u64::U8MAX), U8(_)) => U8(a as u8), + (Infer(a @ 0...as_u64::U16MAX), U16(_)) => U16(a as u16), + (Infer(a @ 0...as_u64::U32MAX), U32(_)) => U32(a as u32), + (Infer(a), U64(_)) => U64(a), + (Infer(a @ 0...as_u64::U32MAX), Usize(Us32(_))) => Usize(Us32(a as u32)), + (Infer(a), Usize(Us64(_))) => Usize(Us64(a)), + + (Infer(_), _) => return Err(ConstMathErr::NotInRange), + + (InferSigned(a @ as_i64::I8MIN...as_i64::I8MAX), I8(_)) => I8(a as i8), + (InferSigned(a @ as_i64::I16MIN...as_i64::I16MAX), I16(_)) => I16(a as i16), + (InferSigned(a @ as_i64::I32MIN...as_i64::I32MAX), I32(_)) => I32(a as i32), + (InferSigned(a), I64(_)) => I64(a), + (InferSigned(a @ as_i64::I32MIN...as_i64::I32MAX), Isize(Is32(_))) => { + Isize(Is32(a as i32)) + }, + (InferSigned(a), Isize(Is64(_))) => Isize(Is64(a)), + (InferSigned(a @ 0...as_i64::U8MAX), U8(_)) => U8(a as u8), + (InferSigned(a @ 0...as_i64::U16MAX), U16(_)) => U16(a as u16), + (InferSigned(a @ 0...as_i64::U32MAX), U32(_)) => U32(a as u32), + (InferSigned(a @ 0...as_i64::I64MAX), U64(_)) => U64(a as u64), + (InferSigned(a @ 0...as_i64::U32MAX), Usize(Us32(_))) => Usize(Us32(a as u32)), + (InferSigned(a @ 0...as_i64::I64MAX), Usize(Us64(_))) => Usize(Us64(a as u64)), + (InferSigned(_), _) => return Err(ConstMathErr::NotInRange), + _ => self, // already known types + }; + Ok((inferred, other)) + } + + /// Turn this value into an `Infer` or an `InferSigned` + pub fn erase_type(self) -> Self { + match self { + Infer(i) => Infer(i), + InferSigned(i) if i < 0 => InferSigned(i), + I8(i) if i < 0 => InferSigned(i as i64), + I16(i) if i < 0 => InferSigned(i as i64), + I32(i) if i < 0 => InferSigned(i as i64), + I64(i) if i < 0 => InferSigned(i as i64), + Isize(Is32(i)) if i < 0 => InferSigned(i as i64), + Isize(Is64(i)) if i < 0 => InferSigned(i as i64), + InferSigned(i) => Infer(i as u64), + I8(i) => Infer(i as u64), + I16(i) => Infer(i as u64), + I32(i) => Infer(i as u64), + I64(i) => Infer(i as u64), + Isize(Is32(i)) => Infer(i as u64), + Isize(Is64(i)) => Infer(i as u64), + U8(i) => Infer(i as u64), + U16(i) => Infer(i as u64), + U32(i) => Infer(i as u64), + U64(i) => Infer(i as u64), + Usize(Us32(i)) => Infer(i as u64), + Usize(Us64(i)) => Infer(i), + } + } + + /// Description of the type, not the value + pub fn description(&self) -> &'static str { + match *self { + Infer(_) => "not yet inferred integral", + InferSigned(_) => "not yet inferred signed integral", + I8(_) => "i8", + I16(_) => "i16", + I32(_) => "i32", + I64(_) => "i64", + Isize(_) => "isize", + U8(_) => "u8", + U16(_) => "u16", + U32(_) => "u32", + U64(_) => "u64", + Usize(_) => "usize", + } + } + + /// Erases the type and returns a u64. + /// This is not the same as `-5i8 as u64` but as `-5i8 as i64 as u64` + pub fn to_u64_unchecked(self) -> u64 { + match self.erase_type() { + ConstInt::Infer(i) => i, + ConstInt::InferSigned(i) => i as u64, + _ => unreachable!(), + } + } + + /// Converts the value to a `u32` if it's in the range 0...std::u32::MAX + pub fn to_u32(&self) -> Option { + match *self { + I8(v) if v >= 0 => Some(v as u32), + I16(v) if v >= 0 => Some(v as u32), + I32(v) if v >= 0 => Some(v as u32), + InferSigned(v) + | Isize(Is64(v)) + | I64(v) if v >= 0 && v <= ::std::u32::MAX as i64 => Some(v as u32), + Isize(Is32(v)) if v >= 0 => Some(v as u32), + U8(v) => Some(v as u32), + U16(v) => Some(v as u32), + U32(v) => Some(v), + Infer(v) + | Usize(Us64(v)) + | U64(v) if v <= ::std::u32::MAX as u64 => Some(v as u32), + Usize(Us32(v)) => Some(v), + _ => None, + } + } + + /// Converts the value to a `u64` if it's >= 0 + pub fn to_u64(&self) -> Option { + match *self { + Infer(v) => Some(v), + InferSigned(v) if v >= 0 => Some(v as u64), + I8(v) if v >= 0 => Some(v as u64), + I16(v) if v >= 0 => Some(v as u64), + I32(v) if v >= 0 => Some(v as u64), + I64(v) if v >= 0 => Some(v as u64), + Isize(Is32(v)) if v >= 0 => Some(v as u64), + Isize(Is64(v)) if v >= 0 => Some(v as u64), + U8(v) => Some(v as u64), + U16(v) => Some(v as u64), + U32(v) => Some(v as u64), + U64(v) => Some(v), + Usize(Us32(v)) => Some(v as u64), + Usize(Us64(v)) => Some(v), + _ => None, + } + } + + pub fn is_negative(&self) -> bool { + match *self { + I8(v) => v < 0, + I16(v) => v < 0, + I32(v) => v < 0, + I64(v) => v < 0, + Isize(Is32(v)) => v < 0, + Isize(Is64(v)) => v < 0, + InferSigned(v) => v < 0, + _ => false, + } + } + + /// Compares the values if they are of the same type + pub fn try_cmp(self, rhs: Self) -> Result<::std::cmp::Ordering, ConstMathErr> { + match try!(self.infer(rhs)) { + (I8(a), I8(b)) => Ok(a.cmp(&b)), + (I16(a), I16(b)) => Ok(a.cmp(&b)), + (I32(a), I32(b)) => Ok(a.cmp(&b)), + (I64(a), I64(b)) => Ok(a.cmp(&b)), + (Isize(Is32(a)), Isize(Is32(b))) => Ok(a.cmp(&b)), + (Isize(Is64(a)), Isize(Is64(b))) => Ok(a.cmp(&b)), + (U8(a), U8(b)) => Ok(a.cmp(&b)), + (U16(a), U16(b)) => Ok(a.cmp(&b)), + (U32(a), U32(b)) => Ok(a.cmp(&b)), + (U64(a), U64(b)) => Ok(a.cmp(&b)), + (Usize(Us32(a)), Usize(Us32(b))) => Ok(a.cmp(&b)), + (Usize(Us64(a)), Usize(Us64(b))) => Ok(a.cmp(&b)), + (Infer(a), Infer(b)) => Ok(a.cmp(&b)), + (InferSigned(a), InferSigned(b)) => Ok(a.cmp(&b)), + _ => Err(CmpBetweenUnequalTypes), + } + } + + /// Adds 1 to the value and wraps around if the maximum for the type is reached + pub fn wrap_incr(self) -> Self { + macro_rules! add1 { + ($e:expr) => { ($e).wrapping_add(1) } + } + match self { + ConstInt::I8(i) => ConstInt::I8(add1!(i)), + ConstInt::I16(i) => ConstInt::I16(add1!(i)), + ConstInt::I32(i) => ConstInt::I32(add1!(i)), + ConstInt::I64(i) => ConstInt::I64(add1!(i)), + ConstInt::Isize(ConstIsize::Is32(i)) => ConstInt::Isize(ConstIsize::Is32(add1!(i))), + ConstInt::Isize(ConstIsize::Is64(i)) => ConstInt::Isize(ConstIsize::Is64(add1!(i))), + ConstInt::U8(i) => ConstInt::U8(add1!(i)), + ConstInt::U16(i) => ConstInt::U16(add1!(i)), + ConstInt::U32(i) => ConstInt::U32(add1!(i)), + ConstInt::U64(i) => ConstInt::U64(add1!(i)), + ConstInt::Usize(ConstUsize::Us32(i)) => ConstInt::Usize(ConstUsize::Us32(add1!(i))), + ConstInt::Usize(ConstUsize::Us64(i)) => ConstInt::Usize(ConstUsize::Us64(add1!(i))), + ConstInt::Infer(_) | ConstInt::InferSigned(_) => panic!("no type info for const int"), + } + } +} + +impl ::std::cmp::PartialOrd for ConstInt { + fn partial_cmp(&self, other: &Self) -> Option { + self.try_cmp(*other).ok() + } +} + +impl ::std::cmp::Ord for ConstInt { + fn cmp(&self, other: &Self) -> Ordering { + self.try_cmp(*other).unwrap() + } +} + +impl ::std::fmt::Display for ConstInt { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + match *self { + Infer(i) => write!(fmt, "{}", i), + InferSigned(i) => write!(fmt, "{}", i), + I8(i) => write!(fmt, "{}i8", i), + I16(i) => write!(fmt, "{}i16", i), + I32(i) => write!(fmt, "{}i32", i), + I64(i) => write!(fmt, "{}i64", i), + Isize(ConstIsize::Is64(i)) => write!(fmt, "{}isize", i), + Isize(ConstIsize::Is32(i)) => write!(fmt, "{}isize", i), + U8(i) => write!(fmt, "{}u8", i), + U16(i) => write!(fmt, "{}u16", i), + U32(i) => write!(fmt, "{}u32", i), + U64(i) => write!(fmt, "{}u64", i), + Usize(ConstUsize::Us64(i)) => write!(fmt, "{}usize", i), + Usize(ConstUsize::Us32(i)) => write!(fmt, "{}usize", i), + } + } +} + +macro_rules! overflowing { + ($e:expr, $err:expr) => {{ + if $e.1 { + return Err(Overflow($err)); + } else { + $e.0 + } + }} +} + +macro_rules! impl_binop { + ($op:ident, $func:ident, $checked_func:ident) => { + impl ::std::ops::$op for ConstInt { + type Output = Result; + fn $func(self, rhs: Self) -> Result { + match try!(self.infer(rhs)) { + (I8(a), I8(b)) => a.$checked_func(b).map(I8), + (I16(a), I16(b)) => a.$checked_func(b).map(I16), + (I32(a), I32(b)) => a.$checked_func(b).map(I32), + (I64(a), I64(b)) => a.$checked_func(b).map(I64), + (Isize(Is32(a)), Isize(Is32(b))) => a.$checked_func(b).map(Is32).map(Isize), + (Isize(Is64(a)), Isize(Is64(b))) => a.$checked_func(b).map(Is64).map(Isize), + (U8(a), U8(b)) => a.$checked_func(b).map(U8), + (U16(a), U16(b)) => a.$checked_func(b).map(U16), + (U32(a), U32(b)) => a.$checked_func(b).map(U32), + (U64(a), U64(b)) => a.$checked_func(b).map(U64), + (Usize(Us32(a)), Usize(Us32(b))) => a.$checked_func(b).map(Us32).map(Usize), + (Usize(Us64(a)), Usize(Us64(b))) => a.$checked_func(b).map(Us64).map(Usize), + (Infer(a), Infer(b)) => a.$checked_func(b).map(Infer), + (InferSigned(a), InferSigned(b)) => a.$checked_func(b).map(InferSigned), + _ => return Err(UnequalTypes(Op::$op)), + }.ok_or(Overflow(Op::$op)) + } + } + } +} + +macro_rules! derive_binop { + ($op:ident, $func:ident) => { + impl ::std::ops::$op for ConstInt { + type Output = Result; + fn $func(self, rhs: Self) -> Result { + match try!(self.infer(rhs)) { + (I8(a), I8(b)) => Ok(I8(a.$func(b))), + (I16(a), I16(b)) => Ok(I16(a.$func(b))), + (I32(a), I32(b)) => Ok(I32(a.$func(b))), + (I64(a), I64(b)) => Ok(I64(a.$func(b))), + (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a.$func(b)))), + (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a.$func(b)))), + (U8(a), U8(b)) => Ok(U8(a.$func(b))), + (U16(a), U16(b)) => Ok(U16(a.$func(b))), + (U32(a), U32(b)) => Ok(U32(a.$func(b))), + (U64(a), U64(b)) => Ok(U64(a.$func(b))), + (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a.$func(b)))), + (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a.$func(b)))), + (Infer(a), Infer(b)) => Ok(Infer(a.$func(b))), + (InferSigned(a), InferSigned(b)) => Ok(InferSigned(a.$func(b))), + _ => Err(UnequalTypes(Op::$op)), + } + } + } + } +} + +impl_binop!(Add, add, checked_add); +impl_binop!(Sub, sub, checked_sub); +impl_binop!(Mul, mul, checked_mul); +derive_binop!(BitAnd, bitand); +derive_binop!(BitOr, bitor); +derive_binop!(BitXor, bitxor); + +fn check_division( + lhs: ConstInt, + rhs: ConstInt, + op: Op, + zerr: ConstMathErr, +) -> Result<(), ConstMathErr> { + match (lhs, rhs) { + (I8(_), I8(0)) => Err(zerr), + (I16(_), I16(0)) => Err(zerr), + (I32(_), I32(0)) => Err(zerr), + (I64(_), I64(0)) => Err(zerr), + (Isize(_), Isize(Is32(0))) => Err(zerr), + (Isize(_), Isize(Is64(0))) => Err(zerr), + (InferSigned(_), InferSigned(0)) => Err(zerr), + + (U8(_), U8(0)) => Err(zerr), + (U16(_), U16(0)) => Err(zerr), + (U32(_), U32(0)) => Err(zerr), + (U64(_), U64(0)) => Err(zerr), + (Usize(_), Usize(Us32(0))) => Err(zerr), + (Usize(_), Usize(Us64(0))) => Err(zerr), + (Infer(_), Infer(0)) => Err(zerr), + + (I8(::std::i8::MIN), I8(-1)) => Err(Overflow(op)), + (I16(::std::i16::MIN), I16(-1)) => Err(Overflow(op)), + (I32(::std::i32::MIN), I32(-1)) => Err(Overflow(op)), + (I64(::std::i64::MIN), I64(-1)) => Err(Overflow(op)), + (Isize(Is32(::std::i32::MIN)), Isize(Is32(-1))) => Err(Overflow(op)), + (Isize(Is64(::std::i64::MIN)), Isize(Is64(-1))) => Err(Overflow(op)), + (InferSigned(::std::i64::MIN), InferSigned(-1)) => Err(Overflow(op)), + + _ => Ok(()), + } +} + +impl ::std::ops::Div for ConstInt { + type Output = Result; + fn div(self, rhs: Self) -> Result { + let (lhs, rhs) = try!(self.infer(rhs)); + try!(check_division(lhs, rhs, Op::Div, DivisionByZero)); + match (lhs, rhs) { + (I8(a), I8(b)) => Ok(I8(a/b)), + (I16(a), I16(b)) => Ok(I16(a/b)), + (I32(a), I32(b)) => Ok(I32(a/b)), + (I64(a), I64(b)) => Ok(I64(a/b)), + (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a/b))), + (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a/b))), + (InferSigned(a), InferSigned(b)) => Ok(InferSigned(a/b)), + + (U8(a), U8(b)) => Ok(U8(a/b)), + (U16(a), U16(b)) => Ok(U16(a/b)), + (U32(a), U32(b)) => Ok(U32(a/b)), + (U64(a), U64(b)) => Ok(U64(a/b)), + (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a/b))), + (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a/b))), + (Infer(a), Infer(b)) => Ok(Infer(a/b)), + + _ => Err(UnequalTypes(Op::Div)), + } + } +} + +impl ::std::ops::Rem for ConstInt { + type Output = Result; + fn rem(self, rhs: Self) -> Result { + let (lhs, rhs) = try!(self.infer(rhs)); + // should INT_MIN%-1 be zero or an error? + try!(check_division(lhs, rhs, Op::Rem, RemainderByZero)); + match (lhs, rhs) { + (I8(a), I8(b)) => Ok(I8(a%b)), + (I16(a), I16(b)) => Ok(I16(a%b)), + (I32(a), I32(b)) => Ok(I32(a%b)), + (I64(a), I64(b)) => Ok(I64(a%b)), + (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a%b))), + (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a%b))), + (InferSigned(a), InferSigned(b)) => Ok(InferSigned(a%b)), + + (U8(a), U8(b)) => Ok(U8(a%b)), + (U16(a), U16(b)) => Ok(U16(a%b)), + (U32(a), U32(b)) => Ok(U32(a%b)), + (U64(a), U64(b)) => Ok(U64(a%b)), + (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a%b))), + (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a%b))), + (Infer(a), Infer(b)) => Ok(Infer(a%b)), + + _ => Err(UnequalTypes(Op::Rem)), + } + } +} + +impl ::std::ops::Shl for ConstInt { + type Output = Result; + fn shl(self, rhs: Self) -> Result { + let b = try!(rhs.to_u32().ok_or(ShiftNegative)); + match self { + I8(a) => Ok(I8(overflowing!(a.overflowing_shl(b), Op::Shl))), + I16(a) => Ok(I16(overflowing!(a.overflowing_shl(b), Op::Shl))), + I32(a) => Ok(I32(overflowing!(a.overflowing_shl(b), Op::Shl))), + I64(a) => Ok(I64(overflowing!(a.overflowing_shl(b), Op::Shl))), + Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shl(b), Op::Shl)))), + Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shl(b), Op::Shl)))), + U8(a) => Ok(U8(overflowing!(a.overflowing_shl(b), Op::Shl))), + U16(a) => Ok(U16(overflowing!(a.overflowing_shl(b), Op::Shl))), + U32(a) => Ok(U32(overflowing!(a.overflowing_shl(b), Op::Shl))), + U64(a) => Ok(U64(overflowing!(a.overflowing_shl(b), Op::Shl))), + Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shl(b), Op::Shl)))), + Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shl(b), Op::Shl)))), + Infer(a) => Ok(Infer(overflowing!(a.overflowing_shl(b), Op::Shl))), + InferSigned(a) => Ok(InferSigned(overflowing!(a.overflowing_shl(b), Op::Shl))), + } + } +} + +impl ::std::ops::Shr for ConstInt { + type Output = Result; + fn shr(self, rhs: Self) -> Result { + let b = try!(rhs.to_u32().ok_or(ShiftNegative)); + match self { + I8(a) => Ok(I8(overflowing!(a.overflowing_shr(b), Op::Shr))), + I16(a) => Ok(I16(overflowing!(a.overflowing_shr(b), Op::Shr))), + I32(a) => Ok(I32(overflowing!(a.overflowing_shr(b), Op::Shr))), + I64(a) => Ok(I64(overflowing!(a.overflowing_shr(b), Op::Shl))), + Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shr(b), Op::Shr)))), + Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shr(b), Op::Shr)))), + U8(a) => Ok(U8(overflowing!(a.overflowing_shr(b), Op::Shr))), + U16(a) => Ok(U16(overflowing!(a.overflowing_shr(b), Op::Shr))), + U32(a) => Ok(U32(overflowing!(a.overflowing_shr(b), Op::Shr))), + U64(a) => Ok(U64(overflowing!(a.overflowing_shr(b), Op::Shr))), + Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shr(b), Op::Shr)))), + Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shr(b), Op::Shr)))), + Infer(a) => Ok(Infer(overflowing!(a.overflowing_shr(b), Op::Shr))), + InferSigned(a) => Ok(InferSigned(overflowing!(a.overflowing_shr(b), Op::Shr))), + } + } +} + +impl ::std::ops::Neg for ConstInt { + type Output = Result; + fn neg(self) -> Result { + match self { + I8(a) => Ok(I8(overflowing!(a.overflowing_neg(), Op::Neg))), + I16(a) => Ok(I16(overflowing!(a.overflowing_neg(), Op::Neg))), + I32(a) => Ok(I32(overflowing!(a.overflowing_neg(), Op::Neg))), + I64(a) => Ok(I64(overflowing!(a.overflowing_neg(), Op::Neg))), + Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_neg(), Op::Neg)))), + Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_neg(), Op::Neg)))), + U8(0) => Ok(U8(0)), + U16(0) => Ok(U16(0)), + U32(0) => Ok(U32(0)), + U64(0) => Ok(U64(0)), + Usize(Us32(0)) => Ok(Usize(Us32(0))), + Usize(Us64(0)) => Ok(Usize(Us64(0))), + U8(_) => Err(UnsignedNegation), + U16(_) => Err(UnsignedNegation), + U32(_) => Err(UnsignedNegation), + U64(_) => Err(UnsignedNegation), + Usize(_) => Err(UnsignedNegation), + Infer(a @ 0...as_u64::I64MAX) => Ok(InferSigned(-(a as i64))), + Infer(_) => Err(Overflow(Op::Neg)), + InferSigned(a) => Ok(InferSigned(overflowing!(a.overflowing_neg(), Op::Neg))), + } + } +} + +impl ::std::ops::Not for ConstInt { + type Output = Result; + fn not(self) -> Result { + match self { + I8(a) => Ok(I8(!a)), + I16(a) => Ok(I16(!a)), + I32(a) => Ok(I32(!a)), + I64(a) => Ok(I64(!a)), + Isize(Is32(a)) => Ok(Isize(Is32(!a))), + Isize(Is64(a)) => Ok(Isize(Is64(!a))), + U8(a) => Ok(U8(!a)), + U16(a) => Ok(U16(!a)), + U32(a) => Ok(U32(!a)), + U64(a) => Ok(U64(!a)), + Usize(Us32(a)) => Ok(Usize(Us32(!a))), + Usize(Us64(a)) => Ok(Usize(Us64(!a))), + Infer(a) => Ok(Infer(!a)), + InferSigned(a) => Ok(InferSigned(!a)), + } + } +} diff --git a/src/librustc_const_eval/is.rs b/src/librustc_const_eval/is.rs new file mode 100644 index 0000000000000..082c6510f8bc9 --- /dev/null +++ b/src/librustc_const_eval/is.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax::ast; +use super::err::*; + +/// Depending on the target only one variant is ever used in a compilation. +/// Anything else is an error. This invariant is checked at several locations +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] +pub enum ConstIsize { + Is32(i32), + Is64(i64), +} +pub use self::ConstIsize::*; + +impl ConstIsize { + pub fn as_i64(self, target_int_ty: ast::IntTy) -> i64 { + match (self, target_int_ty) { + (Is32(i), ast::IntTy::I32) => i as i64, + (Is64(i), ast::IntTy::I64) => i, + _ => panic!("got invalid isize size for target"), + } + } + pub fn new(i: i64, target_int_ty: ast::IntTy) -> Result { + match target_int_ty { + ast::IntTy::I32 if i as i32 as i64 == i => Ok(Is32(i as i32)), + ast::IntTy::I32 => Err(LitOutOfRange(ast::IntTy::Is)), + ast::IntTy::I64 => Ok(Is64(i)), + _ => unreachable!(), + } + } +} diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs new file mode 100644 index 0000000000000..e4c702f643b20 --- /dev/null +++ b/src/librustc_const_eval/lib.rs @@ -0,0 +1,42 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Rusty Mathematics +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![crate_name = "rustc_const_eval"] +#![unstable(feature = "rustc_private", issue = "27812")] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] + + +#![feature(rustc_private)] +#![feature(staged_api)] + +#[macro_use] extern crate log; +#[macro_use] extern crate syntax; + +extern crate serialize as rustc_serialize; // used by deriving + +mod int; +mod us; +mod is; +mod err; + +pub use int::*; +pub use us::*; +pub use is::*; +pub use err::ConstMathErr; diff --git a/src/librustc_const_eval/us.rs b/src/librustc_const_eval/us.rs new file mode 100644 index 0000000000000..e5a7086d43663 --- /dev/null +++ b/src/librustc_const_eval/us.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax::ast; +use super::err::*; + +/// Depending on the target only one variant is ever used in a compilation. +/// Anything else is an error. This invariant is checked at several locations +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)] +pub enum ConstUsize { + Us32(u32), + Us64(u64), +} +pub use self::ConstUsize::*; + +impl ConstUsize { + pub fn as_u64(self, target_uint_ty: ast::UintTy) -> u64 { + match (self, target_uint_ty) { + (Us32(i), ast::UintTy::U32) => i as u64, + (Us64(i), ast::UintTy::U64) => i, + _ => panic!("got invalid usize size for target"), + } + } + pub fn new(i: u64, target_uint_ty: ast::UintTy) -> Result { + match target_uint_ty { + ast::UintTy::U32 if i as u32 as u64 == i => Ok(Us32(i as u32)), + ast::UintTy::U32 => Err(ULitOutOfRange(ast::UintTy::Us)), + ast::UintTy::U64 => Ok(Us64(i)), + _ => unreachable!(), + } + } +} diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 10535549ceb77..4d844bbf032b6 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -143,8 +143,11 @@ impl LateLintPass for TypeLimits { else { false } } else { match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked, None) { - Ok(ConstVal::Int(shift)) => { shift as u64 >= bits }, - Ok(ConstVal::Uint(shift)) => { shift >= bits }, + Ok(ConstVal::Integral(i)) => { + i.is_negative() || i.to_u64() + .map(|i| i >= bits) + .unwrap_or(true) + }, _ => { false } } }; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 06f81a17a061c..dfc794dc5b874 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -35,6 +35,8 @@ use middle::subst; use middle::ty::{ImplContainer, TraitContainer}; use middle::ty::{self, Ty, TyCtxt, TypeFoldable, VariantKind}; +use rustc_const_eval::ConstInt; + use rustc::mir; use rustc::mir::visit::MutVisitor; @@ -198,7 +200,7 @@ fn reexports<'a>(d: rbml::Doc<'a>) -> reader::TaggedDocsIterator<'a> { reader::tagged_docs(d, tag_items_data_item_reexport) } -fn variant_disr_val(d: rbml::Doc) -> Option { +fn variant_disr_val(d: rbml::Doc) -> Option { reader::maybe_get_doc(d, tag_disr_val).and_then(|val_doc| { reader::with_doc_data(val_doc, |data| { str::from_utf8(data).ok().and_then(|s| s.parse().ok()) @@ -396,7 +398,7 @@ pub fn get_adt_def<'tcx>(intr: &IdentInterner, did: did, name: item_name(intr, item), fields: get_variant_fields(intr, cdata, item, tcx), - disr_val: disr, + disr_val: ConstInt::Infer(disr), kind: expect_variant_kind(item_family(item), tcx), } }).collect() @@ -432,7 +434,7 @@ pub fn get_adt_def<'tcx>(intr: &IdentInterner, did: did, name: item_name(intr, doc), fields: get_variant_fields(intr, cdata, doc, tcx), - disr_val: 0, + disr_val: ConstInt::Infer(0), kind: expect_variant_kind(item_family(doc), tcx), } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 1d1cd38225522..3dc0e53b254c4 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -26,6 +26,7 @@ use middle::dependency_format::Linkage; use middle::stability; use middle::subst; use middle::ty::{self, Ty, TyCtxt}; +use middle::ty::util::IntTypeExt; use rustc::back::svh::Svh; use rustc::front::map::{LinkedPath, PathElem, PathElems}; @@ -238,7 +239,8 @@ fn encode_symbol(ecx: &EncodeContext, fn encode_disr_val(_: &EncodeContext, rbml_w: &mut Encoder, disr_val: ty::Disr) { - rbml_w.wr_tagged_str(tag_disr_val, &disr_val.to_string()); + // convert to u64 so just the number is printed, without any type info + rbml_w.wr_tagged_str(tag_disr_val, &disr_val.to_u64_unchecked().to_string()); } fn encode_parent_item(rbml_w: &mut Encoder, id: DefId) { @@ -262,13 +264,14 @@ fn encode_struct_fields(rbml_w: &mut Encoder, fn encode_enum_variant_info<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, - id: NodeId, + did: DefId, vis: hir::Visibility, index: &mut CrateIndex<'tcx>) { - debug!("encode_enum_variant_info(id={})", id); - - let mut disr_val = 0; - let def = ecx.tcx.lookup_adt_def(ecx.tcx.map.local_def_id(id)); + debug!("encode_enum_variant_info(did={:?})", did); + let repr_hints = ecx.tcx.lookup_repr_hints(did); + let repr_type = ecx.tcx.enum_repr_type(repr_hints.get(0)); + let mut disr_val = repr_type.initial_discriminant(&ecx.tcx); + let def = ecx.tcx.lookup_adt_def(did); for variant in &def.variants { let vid = variant.did; let variant_node_id = ecx.local_id(vid); @@ -290,7 +293,7 @@ fn encode_enum_variant_info<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, ty::VariantKind::Unit => 'w', }); encode_name(rbml_w, variant.name); - encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(id)); + encode_parent_item(rbml_w, did); encode_visibility(rbml_w, vis); let attrs = ecx.tcx.get_attrs(vid); @@ -313,7 +316,7 @@ fn encode_enum_variant_info<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, ecx.tcx.map.with_path(variant_node_id, |path| encode_path(rbml_w, path)); rbml_w.end_tag(); - disr_val = disr_val.wrapping_add(1); + disr_val = disr_val.wrap_incr(); } } @@ -1035,7 +1038,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_enum_variant_info(ecx, rbml_w, - item.id, + def_id, vis, index); } diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 5386c5b77c212..f0f0fb847540a 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -36,6 +36,7 @@ extern crate rustc; extern crate rustc_back; extern crate rustc_front; extern crate rustc_llvm; +extern crate rustc_const_eval; pub use rustc::middle; diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index e6430b7d63404..9ecbf748d7c83 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -269,7 +269,7 @@ enum TestKind<'tcx> { // test length of the slice is equal to len Len { - len: usize, + len: u64, op: BinOp, }, } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 02f32da2b83fc..d42c8ff7bd79f 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -84,7 +84,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }; Test { span: match_pair.pattern.span, - kind: TestKind::Len { len: len, op: op }, + kind: TestKind::Len { len: len as u64, op: op }, } } diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 8c435b45daeff..13ab26c358d6a 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -46,7 +46,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { Operand::Constant(constant) } - pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: usize) -> Lvalue<'tcx> { + pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: u64) -> Lvalue<'tcx> { let usize_ty = self.hir.usize_ty(); let temp = self.temp(usize_ty); self.cfg.push_assign_constant( diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 3d14ad2374bb4..6d411b9c07b31 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -94,6 +94,8 @@ use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::mir::repr::*; use syntax::codemap::{Span, DUMMY_SP}; use syntax::parse::token::intern_and_get_ident; +use rustc::middle::const_eval::ConstVal; +use rustc_const_eval::ConstInt; pub struct Scope<'tcx> { extent: CodeExtent, @@ -517,7 +519,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { }, Constant { span: span, ty: self.hir.tcx().types.u32, - literal: self.hir.usize_literal(span_lines.line) + literal: Literal::Value { + value: ConstVal::Integral(ConstInt::U32(span_lines.line as u32)), + }, }) } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index cbd6bed81a68f..1f8a5da9c1b9d 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -10,12 +10,13 @@ use hair::*; use rustc_data_structures::fnv::FnvHashMap; +use rustc_const_eval::ConstInt; use hair::cx::Cx; use hair::cx::block; use hair::cx::to_ref::ToRef; use rustc::front::map; use rustc::middle::def::Def; -use rustc::middle::const_eval; +use rustc::middle::const_eval::{self, ConstVal}; use rustc::middle::region::CodeExtent; use rustc::middle::pat_util; use rustc::middle::ty::{self, VariantDef, Ty}; @@ -227,28 +228,37 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { } } - hir::ExprUnary(op, ref arg) => { + hir::ExprUnary(hir::UnOp::UnNot, ref arg) => { if cx.tcx.is_method_call(self.id) { overloaded_operator(cx, self, ty::MethodCall::expr(self.id), PassArgs::ByValue, arg.to_ref(), vec![]) } else { - // FIXME overflow - let op = match op { - hir::UnOp::UnNot => UnOp::Not, - hir::UnOp::UnNeg => UnOp::Neg, - hir::UnOp::UnDeref => { - cx.tcx.sess.span_bug( - self.span, - "UnDeref should have been handled elsewhere"); - } - }; ExprKind::Unary { - op: op, + op: UnOp::Not, arg: arg.to_ref(), } } } + hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => { + if cx.tcx.is_method_call(self.id) { + overloaded_operator(cx, self, ty::MethodCall::expr(self.id), + PassArgs::ByValue, arg.to_ref(), vec![]) + } else { + // FIXME runtime-overflow + if let hir::ExprLit(_) = arg.node { + ExprKind::Literal { + literal: cx.const_eval_literal(self), + } + } else { + ExprKind::Unary { + op: UnOp::Neg, + arg: arg.to_ref(), + } + } + } + } + hir::ExprStruct(_, ref fields, ref base) => { match expr_ty.sty { ty::TyStruct(adt, substs) => { @@ -338,7 +348,10 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { count: TypedConstVal { ty: cx.tcx.expr_ty(c), span: c.span, - value: const_eval::eval_const_expr(cx.tcx, c) + value: match const_eval::eval_const_expr(cx.tcx, c) { + ConstVal::Integral(ConstInt::Usize(u)) => u, + other => panic!("constant evaluation of repeat count yielded {:?}", other), + }, } }, hir::ExprRet(ref v) => diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index f1b74ca1288f3..fd4cf7c04734a 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -24,6 +24,7 @@ use rustc::middle::ty::{self, Ty, TyCtxt}; use syntax::codemap::Span; use syntax::parse::token; use rustc_front::hir; +use rustc_const_eval::{ConstInt, ConstUsize}; #[derive(Copy, Clone)] pub struct Cx<'a, 'tcx: 'a> { @@ -50,8 +51,11 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { self.tcx.types.usize } - pub fn usize_literal(&mut self, value: usize) -> Literal<'tcx> { - Literal::Value { value: ConstVal::Uint(value as u64) } + pub fn usize_literal(&mut self, value: u64) -> Literal<'tcx> { + match ConstUsize::new(value, self.tcx.sess.target.uint_type) { + Ok(val) => Literal::Value { value: ConstVal::Integral(ConstInt::Usize(val))}, + Err(_) => panic!("usize literal out of range for target"), + } } pub fn bool_ty(&mut self) -> Ty<'tcx> { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 5d915f37f6ebf..7c8c8945bbc91 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -31,6 +31,7 @@ extern crate rustc_data_structures; extern crate rustc_front; extern crate rustc_back; extern crate syntax; +extern crate rustc_const_eval; pub mod build; pub mod graphviz; diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 6f596b15b9214..d7e79e46720b3 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -50,6 +50,7 @@ pub extern crate rustc_llvm as llvm; extern crate rustc_mir; extern crate rustc_platform_intrinsics as intrinsics; extern crate serialize; +extern crate rustc_const_eval; #[macro_use] extern crate log; #[macro_use] extern crate syntax; diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index c5508a8268fe2..320bb1eab3c63 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -1035,7 +1035,7 @@ fn assert_discr_in_range(ity: IntType, min: Disr, max: Disr, discr: Disr) { match ity { attr::UnsignedInt(_) => { assert!(min <= discr); - assert!(discr <= max) + assert!(discr <= max); }, attr::SignedInt(_) => { assert!(min.0 as i64 <= discr.0 as i64); diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 6c47cab64effe..ebe19aa972ce5 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -15,15 +15,7 @@ use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr}; use llvm::{InternalLinkage, ValueRef, Bool, True}; use middle::const_qualif::ConstQualif; use middle::cstore::LOCAL_CRATE; -use middle::const_eval::{self, ConstVal, ConstEvalErr}; -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 middle::const_eval::{self, ConstEvalErr}; use middle::def::Def; use middle::def_id::DefId; use trans::{adt, closure, debuginfo, expr, inline, machine}; @@ -42,9 +34,10 @@ use trans::Disr; use middle::subst::Substs; use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer}; use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer}; -use middle::ty::{self, Ty}; +use middle::ty::{self, Ty, TyCtxt}; use middle::ty::cast::{CastTy,IntTy}; use util::nodemap::NodeMap; +use rustc_const_eval::{ConstInt, ConstMathErr, ConstUsize, ConstIsize}; use rustc_front::hir; @@ -469,35 +462,70 @@ fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty, // Catch this up front by looking for ExprLit directly, // and just accepting it. if let hir::ExprLit(_) = inner_e.node { return Ok(()); } - - let result = match t.sty { - ty::TyInt(int_type) => { - let input = match const_to_opt_int(te) { - Some(v) => v, - None => return Ok(()), - }; - const_int_checked_neg( - input, e, Some(const_eval::IntTy::from(cx.tcx(), int_type))) - } - ty::TyUint(uint_type) => { - let input = match const_to_opt_uint(te) { - Some(v) => v, - None => return Ok(()), - }; - const_uint_checked_neg( - input, e, Some(const_eval::UintTy::from(cx.tcx(), uint_type))) - } - _ => return Ok(()), + let cval = match to_const_int(te, t, cx.tcx()) { + Some(v) => v, + None => return Ok(()), }; - const_err(cx, e, result, trueconst) + match -cval { + Ok(_) => return Ok(()), + Err(err) => const_err(cx, e, Err(err), trueconst), + } } else { Ok(()) } } +fn to_const_int(value: ValueRef, t: Ty, tcx: &TyCtxt) -> Option { + match t.sty { + ty::TyInt(int_type) => const_to_opt_int(value).and_then(|input| match int_type { + ast::IntTy::I8 => { + assert_eq!(input as i8 as i64, input); + Some(ConstInt::I8(input as i8)) + }, + ast::IntTy::I16 => { + assert_eq!(input as i16 as i64, input); + Some(ConstInt::I16(input as i16)) + }, + ast::IntTy::I32 => { + assert_eq!(input as i32 as i64, input); + Some(ConstInt::I32(input as i32)) + }, + ast::IntTy::I64 => { + Some(ConstInt::I64(input)) + }, + ast::IntTy::Is => { + ConstIsize::new(input, tcx.sess.target.int_type) + .ok().map(ConstInt::Isize) + }, + }), + ty::TyUint(uint_type) => const_to_opt_uint(value).and_then(|input| match uint_type { + ast::UintTy::U8 => { + assert_eq!(input as u8 as u64, input); + Some(ConstInt::U8(input as u8)) + }, + ast::UintTy::U16 => { + assert_eq!(input as u16 as u64, input); + Some(ConstInt::U16(input as u16)) + }, + ast::UintTy::U32 => { + assert_eq!(input as u32 as u64, input); + Some(ConstInt::U32(input as u32)) + }, + ast::UintTy::U64 => { + Some(ConstInt::U64(input)) + }, + ast::UintTy::Us => { + ConstUsize::new(input, tcx.sess.target.uint_type) + .ok().map(ConstInt::Usize) + }, + }), + _ => None, + } +} + fn const_err(cx: &CrateContext, e: &hir::Expr, - result: Result, + result: Result, trueconst: TrueConst) -> Result<(), ConstEvalFailure> { match (result, trueconst) { @@ -506,10 +534,12 @@ fn const_err(cx: &CrateContext, Ok(()) }, (Err(err), TrueConst::Yes) => { + let err = ConstEvalErr{ span: e.span, kind: const_eval::ErrKind::Math(err) }; cx.tcx().sess.span_err(e.span, &err.description()); Err(Compiletime(err)) }, (Err(err), TrueConst::No) => { + let err = ConstEvalErr{ span: e.span, kind: const_eval::ErrKind::Math(err) }; cx.tcx().sess.span_warn(e.span, &err.description()); Err(Runtime(err)) }, @@ -520,46 +550,18 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty, te1: ValueRef, te2: ValueRef, trueconst: TrueConst) -> Result<(), ConstEvalFailure> { let b = if let hir::ExprBinary(b, _, _) = e.node { b } else { unreachable!() }; - - let result = match t.sty { - ty::TyInt(int_type) => { - let (lhs, rhs) = match (const_to_opt_int(te1), - const_to_opt_int(te2)) { - (Some(v1), Some(v2)) => (v1, v2), - _ => return Ok(()), - }; - - let opt_ety = Some(const_eval::IntTy::from(cx.tcx(), int_type)); - match b.node { - hir::BiAdd => const_int_checked_add(lhs, rhs, e, opt_ety), - hir::BiSub => const_int_checked_sub(lhs, rhs, e, opt_ety), - hir::BiMul => const_int_checked_mul(lhs, rhs, e, opt_ety), - hir::BiDiv => const_int_checked_div(lhs, rhs, e, opt_ety), - hir::BiRem => const_int_checked_rem(lhs, rhs, e, opt_ety), - hir::BiShl => const_int_checked_shl(lhs, rhs, e, opt_ety), - hir::BiShr => const_int_checked_shr(lhs, rhs, e, opt_ety), - _ => return Ok(()), - } - } - ty::TyUint(uint_type) => { - let (lhs, rhs) = match (const_to_opt_uint(te1), - const_to_opt_uint(te2)) { - (Some(v1), Some(v2)) => (v1, v2), - _ => return Ok(()), - }; - - let opt_ety = Some(const_eval::UintTy::from(cx.tcx(), uint_type)); - match b.node { - hir::BiAdd => const_uint_checked_add(lhs, rhs, e, opt_ety), - hir::BiSub => const_uint_checked_sub(lhs, rhs, e, opt_ety), - hir::BiMul => const_uint_checked_mul(lhs, rhs, e, opt_ety), - hir::BiDiv => const_uint_checked_div(lhs, rhs, e, opt_ety), - hir::BiRem => const_uint_checked_rem(lhs, rhs, e, opt_ety), - hir::BiShl => const_uint_checked_shl(lhs, rhs, e, opt_ety), - hir::BiShr => const_uint_checked_shr(lhs, rhs, e, opt_ety), - _ => return Ok(()), - } - } + let (lhs, rhs) = match (to_const_int(te1, t, cx.tcx()), to_const_int(te2, t, cx.tcx())) { + (Some(v1), Some(v2)) => (v1, v2), + _ => return Ok(()), + }; + let result = match b.node { + hir::BiAdd => lhs + rhs, + hir::BiSub => lhs - rhs, + hir::BiMul => lhs * rhs, + hir::BiDiv => lhs / rhs, + hir::BiRem => lhs % rhs, + hir::BiShl => lhs << rhs, + hir::BiShr => lhs >> rhs, _ => return Ok(()), }; const_err(cx, e, result, trueconst) diff --git a/src/librustc_trans/trans/debuginfo/metadata.rs b/src/librustc_trans/trans/debuginfo/metadata.rs index 0cd1f4e7fbf3e..e22d42e9d28da 100644 --- a/src/librustc_trans/trans/debuginfo/metadata.rs +++ b/src/librustc_trans/trans/debuginfo/metadata.rs @@ -1597,7 +1597,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, llvm::LLVMDIBuilderCreateEnumerator( DIB(cx), name.as_ptr(), - v.disr_val as u64) + v.disr_val.to_u64_unchecked()) } }) .collect(); diff --git a/src/librustc_trans/trans/disr.rs b/src/librustc_trans/trans/disr.rs index 7cb10a8bc44c4..d74c1ac50ad74 100644 --- a/src/librustc_trans/trans/disr.rs +++ b/src/librustc_trans/trans/disr.rs @@ -26,7 +26,7 @@ impl ::std::ops::BitAnd for Disr { impl From<::middle::ty::Disr> for Disr { fn from(i: ::middle::ty::Disr) -> Disr { - Disr(i) + Disr(i.to_u64_unchecked()) } } diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs index a0615a6cf5b18..2c251a4cd578c 100644 --- a/src/librustc_trans/trans/mir/constant.rs +++ b/src/librustc_trans/trans/mir/constant.rs @@ -12,6 +12,7 @@ use back::abi; use llvm::ValueRef; use middle::ty::{Ty, TypeFoldable}; use rustc::middle::const_eval::{self, ConstVal}; +use rustc_const_eval::ConstInt::*; use rustc::mir::repr as mir; use trans::common::{self, BlockAndBuilder, C_bool, C_bytes, C_floating_f64, C_integral, C_str_slice, C_nil, C_undef}; @@ -19,6 +20,7 @@ use trans::consts; use trans::expr; use trans::inline; use trans::type_of; +use trans::type_::Type; use super::operand::{OperandRef, OperandValue}; use super::MirContext; @@ -63,8 +65,24 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match *cv { ConstVal::Float(v) => C_floating_f64(v, llty), ConstVal::Bool(v) => C_bool(ccx, v), - ConstVal::Int(v) => C_integral(llty, v as u64, true), - ConstVal::Uint(v) => C_integral(llty, v, false), + ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true), + ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true), + ConstVal::Integral(I32(v)) => C_integral(Type::i32(ccx), v as u64, true), + ConstVal::Integral(I64(v)) => C_integral(Type::i64(ccx), v as u64, true), + ConstVal::Integral(Isize(v)) => { + let i = v.as_i64(ccx.tcx().sess.target.int_type); + C_integral(Type::int(ccx), i as u64, true) + }, + ConstVal::Integral(U8(v)) => C_integral(Type::i8(ccx), v as u64, false), + ConstVal::Integral(U16(v)) => C_integral(Type::i16(ccx), v as u64, false), + ConstVal::Integral(U32(v)) => C_integral(Type::i32(ccx), v as u64, false), + ConstVal::Integral(U64(v)) => C_integral(Type::i64(ccx), v, false), + ConstVal::Integral(Usize(v)) => { + let u = v.as_u64(ccx.tcx().sess.target.uint_type); + C_integral(Type::int(ccx), u, false) + }, + ConstVal::Integral(Infer(v)) => C_integral(llty, v as u64, false), + ConstVal::Integral(InferSigned(v)) => C_integral(llty, v as u64, true), ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"), ConstVal::Struct(id) | ConstVal::Tuple(id) | @@ -74,6 +92,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { expr::trans(bcx, expr).datum.val }) }, + ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false), ConstVal::Function(_) => C_nil(ccx) } } @@ -99,7 +118,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let substs = bcx.tcx().mk_substs(bcx.monomorphize(&substs)); let def_id = inline::maybe_instantiate_inline(bcx.ccx(), def_id); let expr = const_eval::lookup_const_by_id(bcx.tcx(), def_id, None, Some(substs)) - .expect("def was const, but lookup_const_by_id failed"); + .expect("def was const, but lookup_const_by_id failed").0; // FIXME: this is falling back to translating from HIR. This is not easy to fix, // because we would have somehow adapt const_eval to work on MIR rather than HIR. let d = bcx.with_block(|bcx| { diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index ce10ed425f63a..ea80af14f1f2f 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -11,6 +11,8 @@ use llvm::ValueRef; use rustc::middle::ty::{self, Ty}; use middle::ty::cast::{CastTy, IntTy}; +use middle::const_eval::ConstVal; +use rustc_const_eval::ConstInt; use rustc::mir::repr as mir; use trans::asm; @@ -95,7 +97,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Repeat(ref elem, ref count) => { let tr_elem = self.trans_operand(&bcx, elem); - let size = self.trans_constval(&bcx, &count.value, count.ty).immediate(); + let count = ConstVal::Integral(ConstInt::Usize(count.value)); + let size = self.trans_constval(&bcx, &count, bcx.tcx().types.usize).immediate(); let bcx = bcx.map_block(|block| { let base = expr::get_dataptr(block, dest.llval); tvec::iter_vec_raw(block, base, tr_elem.ty, size, |block, llslot, _| { diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 1938fa7582918..ba56c5d24ab6e 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -65,6 +65,8 @@ use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope, use util::common::{ErrorReported, FN_OUTPUT_NAME}; use util::nodemap::FnvHashSet; +use rustc_const_eval::ConstInt; + use syntax::{abi, ast}; use syntax::codemap::{Span, Pos}; use syntax::errors::DiagnosticBuilder; @@ -1680,22 +1682,16 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>, hir::TyFixedLengthVec(ref ty, ref e) => { let hint = UncheckedExprHint(tcx.types.usize); match const_eval::eval_const_expr_partial(tcx, &e, hint, None) { - Ok(r) => { - match r { - ConstVal::Int(i) => - tcx.mk_array(ast_ty_to_ty(this, rscope, &ty), - i as usize), - ConstVal::Uint(i) => - tcx.mk_array(ast_ty_to_ty(this, rscope, &ty), - i as usize), - _ => { - span_err!(tcx.sess, ast_ty.span, E0249, - "expected constant integer expression \ - for array length"); - this.tcx().types.err - } - } - } + Ok(ConstVal::Integral(ConstInt::Usize(i))) => { + let i = i.as_u64(tcx.sess.target.uint_type); + assert_eq!(i as usize as u64, i); + tcx.mk_array(ast_ty_to_ty(this, rscope, &ty), i as usize) + }, + Ok(val) => { + span_err!(tcx.sess, ast_ty.span, E0249, + "expected usize value for array length, got {}", val.description()); + this.tcx().types.err + }, Err(ref r) => { let mut err = struct_span_err!(tcx.sess, r.span, E0250, "array length constant evaluation error: {}", diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0743c0b9e187b..19731407d9e7a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -94,7 +94,7 @@ use middle::pat_util::{self, pat_id_map}; use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace}; use middle::traits::{self, report_fulfillment_errors}; use middle::ty::{GenericPredicates, TypeScheme}; -use middle::ty::{Disr, ParamTy, ParameterEnvironment}; +use middle::ty::{ParamTy, ParameterEnvironment}; use middle::ty::{LvaluePreference, NoPreference, PreferMutLvalue}; use middle::ty::{self, ToPolyTraitRef, Ty, TyCtxt}; use middle::ty::{MethodCall, MethodCallee}; @@ -102,7 +102,7 @@ use middle::ty::adjustment; use middle::ty::error::TypeError; use middle::ty::fold::{TypeFolder, TypeFoldable}; use middle::ty::relate::TypeRelation; -use middle::ty::util::Representability; +use middle::ty::util::{Representability, IntTypeExt}; use require_c_abi_if_variadic; use rscope::{ElisionFailureInfo, RegionScope}; use session::{Session, CompileResult}; @@ -4076,34 +4076,6 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, sp: Span, vs: &'tcx [hir::Variant], id: ast::NodeId) { - // disr_in_range should be removed once we have forced type hints for consts - fn disr_in_range(ccx: &CrateCtxt, - ty: attr::IntType, - disr: ty::Disr) -> bool { - fn uint_in_range(ccx: &CrateCtxt, ty: ast::UintTy, disr: ty::Disr) -> bool { - match ty { - ast::UintTy::U8 => disr as u8 as Disr == disr, - ast::UintTy::U16 => disr as u16 as Disr == disr, - ast::UintTy::U32 => disr as u32 as Disr == disr, - ast::UintTy::U64 => disr as u64 as Disr == disr, - ast::UintTy::Us => uint_in_range(ccx, ccx.tcx.sess.target.uint_type, disr) - } - } - fn int_in_range(ccx: &CrateCtxt, ty: ast::IntTy, disr: ty::Disr) -> bool { - match ty { - ast::IntTy::I8 => disr as i8 as Disr == disr, - ast::IntTy::I16 => disr as i16 as Disr == disr, - ast::IntTy::I32 => disr as i32 as Disr == disr, - ast::IntTy::I64 => disr as i64 as Disr == disr, - ast::IntTy::Is => int_in_range(ccx, ccx.tcx.sess.target.int_type, disr) - } - } - match ty { - attr::UnsignedInt(ty) => uint_in_range(ccx, ty, disr), - attr::SignedInt(ty) => int_in_range(ccx, ty, disr) - } - } - fn do_check<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, vs: &'tcx [hir::Variant], id: ast::NodeId, @@ -4117,7 +4089,7 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, let inh = static_inherited_fields(ccx, &tables); let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), id); - let (_, repr_type_ty) = ccx.tcx.enum_repr_type(Some(&hint)); + let repr_type_ty = ccx.tcx.enum_repr_type(Some(&hint)).to_ty(&ccx.tcx); for v in vs { if let Some(ref e) = v.node.disr_expr { check_const_with_ty(&fcx, e.span, e, repr_type_ty); @@ -4142,23 +4114,7 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } None => {} } - // Check for unrepresentable discriminant values - match hint { - attr::ReprAny | attr::ReprExtern => { - disr_vals.push(current_disr_val); - } - attr::ReprInt(sp, ity) => { - if !disr_in_range(ccx, ity, current_disr_val) { - let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0082, - "discriminant value outside specified type"); - span_note!(&mut err, sp, - "discriminant type specified here"); - err.emit(); - } - } - // Error reported elsewhere. - attr::ReprSimd | attr::ReprPacked => {} - } + disr_vals.push(current_disr_val); } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 3ce03e245785a..e0d4e128cf5cc 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -80,6 +80,8 @@ use util::common::{ErrorReported, MemoizationMap}; use util::nodemap::{FnvHashMap, FnvHashSet}; use write_ty_to_tcx; +use rustc_const_eval::ConstInt; + use std::cell::RefCell; use std::collections::HashSet; use std::rc::Rc; @@ -1021,7 +1023,7 @@ fn convert_struct_def<'tcx>(tcx: &TyCtxt<'tcx>, tcx.intern_adt_def( did, ty::AdtKind::Struct, - vec![convert_struct_variant(tcx, ctor_id, it.name, 0, def)] + vec![convert_struct_variant(tcx, ctor_id, it.name, ConstInt::Infer(0), def)] ) } @@ -1030,24 +1032,39 @@ fn convert_enum_def<'tcx>(tcx: &TyCtxt<'tcx>, def: &hir::EnumDef) -> ty::AdtDefMaster<'tcx> { + fn print_err(tcx: &TyCtxt, span: Span, ty: ty::Ty, cv: ConstVal) { + span_err!(tcx.sess, span, E0079, "mismatched types: expected `{}` got `{}`", + ty, cv.description()); + } fn evaluate_disr_expr<'tcx>(tcx: &TyCtxt<'tcx>, - repr_ty: Ty<'tcx>, + repr_ty: attr::IntType, e: &hir::Expr) -> Option { debug!("disr expr, checking {}", pprust::expr_to_string(e)); - let hint = UncheckedExprHint(repr_ty); + let ty_hint = repr_ty.to_ty(tcx); + let hint = UncheckedExprHint(ty_hint); match const_eval::eval_const_expr_partial(tcx, e, hint, None) { - Ok(ConstVal::Int(val)) => Some(val as ty::Disr), - Ok(ConstVal::Uint(val)) => Some(val as ty::Disr), - Ok(_) => { - let sign_desc = if repr_ty.is_signed() { - "signed" - } else { - "unsigned" - }; - span_err!(tcx.sess, e.span, E0079, - "expected {} integer constant", - sign_desc); + Ok(ConstVal::Integral(i)) => { + // FIXME: eval_const_expr_partial should return an error if the hint is wrong + match (repr_ty, i) { + (attr::SignedInt(ast::IntTy::I8), ConstInt::I8(_)) => Some(i), + (attr::SignedInt(ast::IntTy::I16), ConstInt::I16(_)) => Some(i), + (attr::SignedInt(ast::IntTy::I32), ConstInt::I32(_)) => Some(i), + (attr::SignedInt(ast::IntTy::I64), ConstInt::I64(_)) => Some(i), + (attr::SignedInt(ast::IntTy::Is), ConstInt::Isize(_)) => Some(i), + (attr::UnsignedInt(ast::UintTy::U8), ConstInt::U8(_)) => Some(i), + (attr::UnsignedInt(ast::UintTy::U16), ConstInt::U16(_)) => Some(i), + (attr::UnsignedInt(ast::UintTy::U32), ConstInt::U32(_)) => Some(i), + (attr::UnsignedInt(ast::UintTy::U64), ConstInt::U64(_)) => Some(i), + (attr::UnsignedInt(ast::UintTy::Us), ConstInt::Usize(_)) => Some(i), + (_, i) => { + print_err(tcx, e.span, ty_hint, ConstVal::Integral(i)); + None + }, + } + }, + Ok(cv) => { + print_err(tcx, e.span, ty_hint, cv); None }, Err(err) => { @@ -1066,16 +1083,11 @@ fn convert_enum_def<'tcx>(tcx: &TyCtxt<'tcx>, fn report_discrim_overflow(tcx: &TyCtxt, variant_span: Span, variant_name: &str, - repr_type: attr::IntType, prev_val: ty::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(tcx); span_err!(tcx.sess, variant_span, E0370, - "enum discriminant overflowed on value after {}: {}; \ + "enum discriminant overflowed on value after {}; \ set explicitly via {} = {} if that is desired outcome", - prev_val, repr_type, variant_name, computed_value); + prev_val, variant_name, prev_val.wrap_incr()); } fn next_disr(tcx: &TyCtxt, @@ -1085,12 +1097,11 @@ fn convert_enum_def<'tcx>(tcx: &TyCtxt<'tcx>, if let Some(prev_disr_val) = prev_disr_val { let result = repr_type.disr_incr(prev_disr_val); if let None = result { - report_discrim_overflow(tcx, v.span, &v.node.name.as_str(), - repr_type, prev_disr_val); + report_discrim_overflow(tcx, v.span, &v.node.name.as_str(), prev_disr_val); } result } else { - Some(ty::INITIAL_DISCRIMINANT_VALUE) + Some(repr_type.initial_discriminant(tcx)) } } fn convert_enum_variant<'tcx>(tcx: &TyCtxt<'tcx>, @@ -1104,17 +1115,19 @@ fn convert_enum_def<'tcx>(tcx: &TyCtxt<'tcx>, } let did = tcx.map.local_def_id(it.id); let repr_hints = tcx.lookup_repr_hints(did); - let (repr_type, repr_type_ty) = tcx.enum_repr_type(repr_hints.get(0)); + let repr_type = tcx.enum_repr_type(repr_hints.get(0)); let mut prev_disr = None; let variants = def.variants.iter().map(|v| { let disr = match v.node.disr_expr { - Some(ref e) => evaluate_disr_expr(tcx, repr_type_ty, e), + Some(ref e) => evaluate_disr_expr(tcx, repr_type, e), None => next_disr(tcx, v, repr_type, prev_disr) - }.unwrap_or(repr_type.disr_wrap_incr(prev_disr)); + }.unwrap_or_else(|| { + prev_disr.map(ty::Disr::wrap_incr) + .unwrap_or(repr_type.initial_discriminant(tcx)) + }); - let v = convert_enum_variant(tcx, v, disr); prev_disr = Some(disr); - v + convert_enum_variant(tcx, v, disr) }).collect(); tcx.intern_adt_def(tcx.map.local_def_id(it.id), ty::AdtKind::Enum, variants) } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 035f8c6050022..125c3d426a803 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -91,6 +91,7 @@ extern crate rustc; extern crate rustc_platform_intrinsics as intrinsics; extern crate rustc_front; extern crate rustc_back; +extern crate rustc_const_eval; pub use rustc::dep_graph; pub use rustc::front; diff --git a/src/test/auxiliary/dummy_mir_pass.rs b/src/test/auxiliary/dummy_mir_pass.rs index 16ef965e0dbd5..5206f69337290 100644 --- a/src/test/auxiliary/dummy_mir_pass.rs +++ b/src/test/auxiliary/dummy_mir_pass.rs @@ -16,6 +16,7 @@ #[macro_use] extern crate rustc; extern crate rustc_front; extern crate rustc_plugin; +extern crate rustc_const_eval; extern crate syntax; use rustc::mir::transform::MirPass; @@ -23,6 +24,7 @@ use rustc::mir::repr::{Mir, Literal}; use rustc::mir::visit::MutVisitor; use rustc::middle::infer::InferCtxt; use rustc::middle::const_eval::ConstVal; +use rustc_const_eval::ConstInt; use rustc_plugin::Registry; struct Pass; @@ -37,8 +39,10 @@ struct Visitor; impl<'tcx> MutVisitor<'tcx> for Visitor { fn visit_literal(&mut self, literal: &mut Literal<'tcx>) { - if let Literal::Value { value: ConstVal::Int(ref mut i @ 11) } = *literal { - *i = 42; + if let Literal::Value { ref mut value } = *literal { + if let ConstVal::Integral(ConstInt::I32(ref mut i @ 11)) = *value { + *i = 42; + } } } } diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index be67e06d99fee..4d156a49192fd 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -25,9 +25,9 @@ fn main() { //~^ WARN attempted to add with overflow //~^^ WARN attempted to add with overflow let c = 200u8 * 4; - //~^ WARN attempted to mul with overflow + //~^ WARN attempted to multiply with overflow let d = 42u8 - (42u8 + 1); - //~^ WARN attempted to sub with overflow + //~^ WARN attempted to subtract with overflow let _e = BLA; black_box(a); black_box(b); diff --git a/src/test/compile-fail/const-eval-overflow-2.rs b/src/test/compile-fail/const-eval-overflow-2.rs index be04bc9bd3b73..07e27a7dc9a9a 100644 --- a/src/test/compile-fail/const-eval-overflow-2.rs +++ b/src/test/compile-fail/const-eval-overflow-2.rs @@ -21,10 +21,11 @@ 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 +//~| ERROR attempted to negate with overflow fn main() { match -128i8 { - NEG_NEG_128 => println!("A"), + NEG_NEG_128 => println!("A"), //~ NOTE in pattern here _ => println!("B"), } } diff --git a/src/test/compile-fail/const-eval-overflow-3.rs b/src/test/compile-fail/const-eval-overflow-3.rs index c2bc5b2648af3..33fa4aae61151 100644 --- a/src/test/compile-fail/const-eval-overflow-3.rs +++ b/src/test/compile-fail/const-eval-overflow-3.rs @@ -17,7 +17,7 @@ // 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 +// error-pattern: expected constant integer for repeat count, but tried to add two integrals of #![allow(unused_imports)] @@ -36,4 +36,3 @@ fn main() { fn foo(x: T) { println!("{:?}", x); } - diff --git a/src/test/compile-fail/const-eval-overflow-4b.rs b/src/test/compile-fail/const-eval-overflow-4b.rs index 253285d3919c2..68ef1b4775189 100644 --- a/src/test/compile-fail/const-eval-overflow-4b.rs +++ b/src/test/compile-fail/const-eval-overflow-4b.rs @@ -21,8 +21,7 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_T : [u32; (i8::MAX as i8 + 1u8) as usize] - //~^ ERROR mismatched types - //~| ERROR the trait `core::ops::Add` is not implemented for the type `i8` + //~^ ERROR tried to add two integrals of different types [E0250] = [0; (i8::MAX as usize) + 1]; fn main() { diff --git a/src/test/compile-fail/const-eval-overflow.rs b/src/test/compile-fail/const-eval-overflow.rs index 2a2fc2ef080db..3dfcb5bb29a24 100644 --- a/src/test/compile-fail/const-eval-overflow.rs +++ b/src/test/compile-fail/const-eval-overflow.rs @@ -23,84 +23,84 @@ const VALS_I8: (i8, i8, i8, i8) = (-i8::MIN, //~^ ERROR attempted to negate with overflow i8::MIN - 1, - //~^ ERROR attempted to sub with overflow + //~^ ERROR attempted to subtract with overflow i8::MAX + 1, //~^ ERROR attempted to add with overflow i8::MIN * 2, - //~^ ERROR attempted to mul with overflow + //~^ ERROR attempted to multiply 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 + //~^ ERROR attempted to subtract with overflow i16::MAX + 1, //~^ ERROR attempted to add with overflow i16::MIN * 2, - //~^ ERROR attempted to mul with overflow + //~^ ERROR attempted to multiply 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 + //~^ ERROR attempted to subtract with overflow i32::MAX + 1, //~^ ERROR attempted to add with overflow i32::MIN * 2, - //~^ ERROR attempted to mul with overflow + //~^ ERROR attempted to multiply 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 + //~^ ERROR attempted to subtract with overflow i64::MAX + 1, //~^ ERROR attempted to add with overflow i64::MAX * 2, - //~^ ERROR attempted to mul with overflow + //~^ ERROR attempted to multiply with overflow ); const VALS_U8: (u8, u8, u8, u8) = (-(u8::MIN as i8) as u8, u8::MIN - 1, - //~^ ERROR attempted to sub with overflow + //~^ ERROR attempted to subtract with overflow u8::MAX + 1, //~^ ERROR attempted to add with overflow u8::MAX * 2, - //~^ ERROR attempted to mul with overflow + //~^ ERROR attempted to multiply with overflow ); const VALS_U16: (u16, u16, u16, u16) = (-(u16::MIN as i16) as u16, u16::MIN - 1, - //~^ ERROR attempted to sub with overflow + //~^ ERROR attempted to subtract with overflow u16::MAX + 1, //~^ ERROR attempted to add with overflow u16::MAX * 2, - //~^ ERROR attempted to mul with overflow + //~^ ERROR attempted to multiply with overflow ); const VALS_U32: (u32, u32, u32, u32) = (-(u32::MIN as i32) as u32, u32::MIN - 1, - //~^ ERROR attempted to sub with overflow + //~^ ERROR attempted to subtract with overflow u32::MAX + 1, //~^ ERROR attempted to add with overflow u32::MAX * 2, - //~^ ERROR attempted to mul with overflow + //~^ ERROR attempted to multiply with overflow ); const VALS_U64: (u64, u64, u64, u64) = (-(u64::MIN as i64) as u64, u64::MIN - 1, - //~^ ERROR attempted to sub with overflow + //~^ ERROR attempted to subtract with overflow u64::MAX + 1, //~^ ERROR attempted to add with overflow u64::MAX * 2, - //~^ ERROR attempted to mul with overflow + //~^ ERROR attempted to multiply with overflow ); fn main() { diff --git a/src/test/compile-fail/const-integer-bool-ops.rs b/src/test/compile-fail/const-integer-bool-ops.rs index 37a93ec954d8d..0d6cf3bab453f 100644 --- a/src/test/compile-fail/const-integer-bool-ops.rs +++ b/src/test/compile-fail/const-integer-bool-ops.rs @@ -8,32 +8,30 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -const X: usize = 42 && 39; //~ ERROR: can't do this op on unsigned integrals +const X: usize = 42 && 39; //~ ERROR: can't do this op on integrals const ARR: [i32; X] = [99; 34]; //~ NOTE: for array length here -const X1: usize = 42 || 39; //~ ERROR: can't do this op on unsigned integrals +const X1: usize = 42 || 39; //~ ERROR: can't do this op on integrals const ARR1: [i32; X1] = [99; 47]; //~ NOTE: for array length here -// FIXME: the error should be `on signed integrals` -const X2: usize = -42 || -39; //~ ERROR: can't do this op on unsigned integrals +const X2: usize = -42 || -39; //~ ERROR: unary negation of unsigned integer const ARR2: [i32; X2] = [99; 18446744073709551607]; //~ NOTE: for array length here -// FIXME: the error should be `on signed integrals` -const X3: usize = -42 && -39; //~ ERROR: can't do this op on unsigned integrals +const X3: usize = -42 && -39; //~ ERROR: unary negation of unsigned integer const ARR3: [i32; X3] = [99; 6]; //~ NOTE: for array length here const Y: usize = 42.0 == 42.0; -const ARRR: [i32; Y] = [99; 1]; //~ ERROR: expected constant integer expression for array length +const ARRR: [i32; Y] = [99; 1]; //~ ERROR: expected usize value for array length const Y1: usize = 42.0 >= 42.0; -const ARRR1: [i32; Y] = [99; 1]; //~ ERROR: expected constant integer expression for array length +const ARRR1: [i32; Y] = [99; 1]; //~ ERROR: expected usize value for array length const Y2: usize = 42.0 <= 42.0; -const ARRR2: [i32; Y] = [99; 1]; //~ ERROR: expected constant integer expression for array length +const ARRR2: [i32; Y] = [99; 1]; //~ ERROR: expected usize value for array length const Y3: usize = 42.0 > 42.0; -const ARRR3: [i32; Y] = [99; 0]; //~ ERROR: expected constant integer expression for array length +const ARRR3: [i32; Y] = [99; 0]; //~ ERROR: expected usize value for array length const Y4: usize = 42.0 < 42.0; -const ARRR4: [i32; Y] = [99; 0]; //~ ERROR: expected constant integer expression for array length +const ARRR4: [i32; Y] = [99; 0]; //~ ERROR: expected usize value for array length const Y5: usize = 42.0 != 42.0; -const ARRR5: [i32; Y] = [99; 0]; //~ ERROR: expected constant integer expression for array length +const ARRR5: [i32; Y] = [99; 0]; //~ ERROR: expected usize value for array length fn main() { let _ = ARR; diff --git a/src/test/compile-fail/const-len-underflow-separate-spans.rs b/src/test/compile-fail/const-len-underflow-separate-spans.rs index 786c72b66f36f..9c6b774b99039 100644 --- a/src/test/compile-fail/const-len-underflow-separate-spans.rs +++ b/src/test/compile-fail/const-len-underflow-separate-spans.rs @@ -15,7 +15,7 @@ const ONE: usize = 1; const TWO: usize = 2; const LEN: usize = ONE - TWO; -//~^ ERROR array length constant evaluation error: attempted to sub with overflow [E0250] +//~^ ERROR array length constant evaluation error: attempted to subtract with overflow [E0250] fn main() { let a: [i8; LEN] = unimplemented!(); diff --git a/src/test/compile-fail/const-len-underflow-subspans.rs b/src/test/compile-fail/const-len-underflow-subspans.rs index 020717dc1e18f..d51f31087d0df 100644 --- a/src/test/compile-fail/const-len-underflow-subspans.rs +++ b/src/test/compile-fail/const-len-underflow-subspans.rs @@ -16,5 +16,5 @@ const TWO: usize = 2; fn main() { let a: [i8; ONE - TWO] = unimplemented!(); - //~^ ERROR array length constant evaluation error: attempted to sub with overflow [E0250] + //~^ ERROR array length constant evaluation error: attempted to subtract with overflow [E0250] } diff --git a/src/test/compile-fail/const-tup-index-span.rs b/src/test/compile-fail/const-tup-index-span.rs index 8c607fc7e32b2..9d3c432d14878 100644 --- a/src/test/compile-fail/const-tup-index-span.rs +++ b/src/test/compile-fail/const-tup-index-span.rs @@ -11,7 +11,7 @@ // Test spans of errors const TUP: (usize,) = 5 << 64; -//~^ ERROR: attempted left shift with overflow [E0250] +//~^ ERROR: attempted to shift left with overflow [E0250] const ARR: [i32; TUP.0] = []; fn main() { diff --git a/src/test/compile-fail/discrim-overflow-2.rs b/src/test/compile-fail/discrim-overflow-2.rs index 76378d5c8021b..0ff740212e8fa 100644 --- a/src/test/compile-fail/discrim-overflow-2.rs +++ b/src/test/compile-fail/discrim-overflow-2.rs @@ -24,7 +24,7 @@ fn f_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 + OhNo, //~ ERROR enum discriminant overflowed on value after 127i8; set explicitly via OhNo = -128i8 if that is desired outcome } } @@ -33,7 +33,7 @@ fn f_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 + OhNo, //~ ERROR enum discriminant overflowed on value after 255u8; set explicitly via OhNo = 0u8 if that is desired outcome } } diff --git a/src/test/compile-fail/discrim-overflow.rs b/src/test/compile-fail/discrim-overflow.rs index 5d7e61e9d1eec..7316e737b6da8 100644 --- a/src/test/compile-fail/discrim-overflow.rs +++ b/src/test/compile-fail/discrim-overflow.rs @@ -22,7 +22,7 @@ fn f_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 + OhNo, //~ ERROR enum discriminant overflowed on value after 127i8; set explicitly via OhNo = -128i8 if that is desired outcome } let x = A::Ok; @@ -33,7 +33,7 @@ fn f_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 + OhNo, //~ ERROR enum discriminant overflowed on value after 255u8; set explicitly via OhNo = 0u8 if that is desired outcome } let x = A::Ok; diff --git a/src/test/compile-fail/enum-discrim-too-small.rs b/src/test/compile-fail/enum-discrim-too-small.rs index 84a27a3820069..d6ba09bb4c5bf 100644 --- a/src/test/compile-fail/enum-discrim-too-small.rs +++ b/src/test/compile-fail/enum-discrim-too-small.rs @@ -9,46 +9,32 @@ // except according to those terms. -#[repr(u8)] //~ NOTE discriminant type specified here +#[repr(u8)] enum Eu8 { Au8 = 23, Bu8 = 223, - Cu8 = -23, //~ ERROR discriminant value outside specified type + Cu8 = -23, //~ ERROR unary negation of unsigned integer } -#[repr(i8)] //~ NOTE discriminant type specified here -enum Ei8 { - Ai8 = 23, - Bi8 = -23, - Ci8 = 223, //~ ERROR discriminant value outside specified type -} - -#[repr(u16)] //~ NOTE discriminant type specified here +#[repr(u16)] enum Eu16 { Au16 = 23, Bu16 = 55555, - Cu16 = -22333, //~ ERROR discriminant value outside specified type -} - -#[repr(i16)] //~ NOTE discriminant type specified here -enum Ei16 { - Ai16 = 23, - Bi16 = -22333, - Ci16 = 55555, //~ ERROR discriminant value outside specified type + Cu16 = -22333, //~ ERROR unary negation of unsigned integer } -#[repr(u32)] //~ NOTE discriminant type specified here +#[repr(u32)] enum Eu32 { Au32 = 23, Bu32 = 3_000_000_000, - Cu32 = -2_000_000_000, //~ ERROR discriminant value outside specified type + Cu32 = -2_000_000_000, //~ ERROR unary negation of unsigned integer } -#[repr(i32)] //~ NOTE discriminant type specified here -enum Ei32 { - Ai32 = 23, - Bi32 = -2_000_000_000, - Ci32 = 3_000_000_000, //~ ERROR discriminant value outside specified type +#[repr(u64)] +enum Eu64 { + Au32 = 23, + Bu32 = 3_000_000_000, + Cu32 = -2_000_000_000, //~ ERROR unary negation of unsigned integer } // u64 currently allows negative numbers, and i64 allows numbers greater than `1<<63`. This is a diff --git a/src/test/compile-fail/enum-discrim-too-small2.rs b/src/test/compile-fail/enum-discrim-too-small2.rs new file mode 100644 index 0000000000000..a25d0a9e1871f --- /dev/null +++ b/src/test/compile-fail/enum-discrim-too-small2.rs @@ -0,0 +1,44 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[repr(i8)] +enum Ei8 { + Ai8 = 23, + Bi8 = -23, + Ci8 = 223, //~ ERROR literal out of range for i8 [E0080] +} + +#[repr(i16)] +enum Ei16 { + Ai16 = 23, + Bi16 = -22333, + Ci16 = 55555, //~ ERROR literal out of range for i16 [E0080] +} + +#[repr(i32)] +enum Ei32 { + Ai32 = 23, + Bi32 = -2_000_000_000, + Ci32 = 3_000_000_000, //~ ERROR literal out of range for i32 [E0080] +} + +#[repr(i64)] +enum Ei64 { + Ai64 = 23, + Bi64 = -9223372036854775808, + Ci64 = 9223372036854775809, //~ ERROR literal out of range for i64 [E0080] +} + +// u64 currently allows negative numbers, and i64 allows numbers greater than `1<<63`. This is a +// little counterintuitive, but since the discriminant can store all the bits, and extracting it +// with a cast requires specifying the signedness, there is no loss of information in those cases. +// This also applies to isize and usize on 64-bit targets. + +pub fn main() { } diff --git a/src/test/compile-fail/eval-enum.rs b/src/test/compile-fail/eval-enum.rs index ed1327f31185e..7ca274b81e574 100644 --- a/src/test/compile-fail/eval-enum.rs +++ b/src/test/compile-fail/eval-enum.rs @@ -10,7 +10,8 @@ enum test { div_zero = 1/0, //~ERROR constant evaluation error: attempted to divide by zero - rem_zero = 1%0 //~ERROR constant evaluation error: attempted remainder with a divisor of zero + rem_zero = 1%0, +//~^ ERROR constant evaluation error: attempted to calculate the remainder with a divisor of zero } fn main() {} diff --git a/src/test/compile-fail/feature-gate-negate-unsigned.rs b/src/test/compile-fail/feature-gate-negate-unsigned.rs index 15cc17b19db33..546fc5e3c6063 100644 --- a/src/test/compile-fail/feature-gate-negate-unsigned.rs +++ b/src/test/compile-fail/feature-gate-negate-unsigned.rs @@ -21,22 +21,7 @@ const _MAX: usize = -1; //~| HELP use a cast or the `!` operator fn main() { - let a = -1; - //~^ ERROR unary negation of unsigned integer - //~| HELP use a cast or the `!` operator - let _b : u8 = a; // for infering variable a to u8. - - -a; - //~^ ERROR unary negation of unsigned integer - //~| HELP use a cast or the `!` operator - - let _d = -1u8; - //~^ ERROR unary negation of unsigned integer - //~| HELP use a cast or the `!` operator - - for _ in -10..10u8 {} - //~^ ERROR unary negation of unsigned integer - //~| HELP use a cast or the `!` operator - + let x = 5u8; + let _y = -x; //~ ERROR unary negation of unsigned integer -S; // should not trigger the gate; issue 26840 } diff --git a/src/test/compile-fail/feature-gate-negate-unsigned0.rs b/src/test/compile-fail/feature-gate-negate-unsigned0.rs new file mode 100644 index 0000000000000..05b194345d405 --- /dev/null +++ b/src/test/compile-fail/feature-gate-negate-unsigned0.rs @@ -0,0 +1,31 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that negating unsigned integers doesn't compile + +struct S; +impl std::ops::Neg for S { + type Output = u32; + fn neg(self) -> u32 { 0 } +} + +fn main() { + let a = -1; + //~^ ERROR unary negation of unsigned integer + let _b : u8 = a; // for infering variable a to u8. + + let _d = -1u8; + //~^ ERROR unary negation of unsigned integer + + for _ in -10..10u8 {} + //~^ ERROR unary negation of unsigned integer + + -S; // should not trigger the gate; issue 26840 +} diff --git a/src/test/compile-fail/issue-15524.rs b/src/test/compile-fail/issue-15524.rs index b378d2f885e83..bdf344dcdfe8d 100644 --- a/src/test/compile-fail/issue-15524.rs +++ b/src/test/compile-fail/issue-15524.rs @@ -12,12 +12,12 @@ const N: isize = 1; enum Foo { A = 1, - B = 1, //~ ERROR discriminant value `1` already exists + B = 1, //~ ERROR discriminant value `1isize` already exists //~^^ NOTE conflicting C = 0, - D, //~ ERROR discriminant value `1` already exists + D, //~ ERROR discriminant value `1isize` already exists //~^^^^^ NOTE conflicting - E = N, //~ ERROR discriminant value `1` already exists + E = N, //~ ERROR discriminant value `1isize` already exists //~^^^^^^^ NOTE conflicting } diff --git a/src/test/compile-fail/issue-8460-const.rs b/src/test/compile-fail/issue-8460-const.rs index 95921556c7db1..fe51d0b69987a 100644 --- a/src/test/compile-fail/issue-8460-const.rs +++ b/src/test/compile-fail/issue-8460-const.rs @@ -35,23 +35,23 @@ fn main() { assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); //~^ ERROR attempted to divide by zero assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow + //~^ ERROR attempted to calculate the remainder with overflow assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow + //~^ ERROR attempted to calculate the remainder with overflow assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow + //~^ ERROR attempted to calculate the remainder with overflow assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow + //~^ ERROR attempted to calculate the remainder with overflow assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); - //~^ ERROR attempted remainder with overflow + //~^ ERROR attempted to calculate the remainder with overflow assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero + //~^ ERROR attempted to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero + //~^ ERROR attempted to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero + //~^ ERROR attempted to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero + //~^ ERROR attempted to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); - //~^ ERROR attempted remainder with a divisor of zero + //~^ ERROR attempted to calculate the remainder with a divisor of zero } diff --git a/src/test/compile-fail/issue-8761.rs b/src/test/compile-fail/issue-8761.rs index 35be01970cb56..da8bd1dc28d98 100644 --- a/src/test/compile-fail/issue-8761.rs +++ b/src/test/compile-fail/issue-8761.rs @@ -10,13 +10,9 @@ enum Foo { A = 1i64, - //~^ ERROR mismatched types - //~| expected `isize` - //~| found `i64` + //~^ ERROR mismatched types: expected `isize` got `i64` B = 2u8 - //~^ ERROR mismatched types - //~| expected `isize` - //~| found `u8` + //~^ ERROR mismatched types: expected `isize` got `u8` } fn main() {} diff --git a/src/test/compile-fail/lint-type-limits.rs b/src/test/compile-fail/lint-type-limits.rs index 0b414ad73db6f..29929c120c30f 100644 --- a/src/test/compile-fail/lint-type-limits.rs +++ b/src/test/compile-fail/lint-type-limits.rs @@ -24,11 +24,6 @@ fn bar() -> i8 { return 123; } -fn baz() -> bool { - 128 > bar() //~ ERROR comparison is useless due to type limits - //~^ WARNING literal out of range for i8 -} - fn bleh() { let u = 42u8; let _ = u > 255; //~ ERROR comparison is useless due to type limits @@ -40,11 +35,3 @@ fn bleh() { let _ = u >= 0; //~ ERROR comparison is useless due to type limits let _ = 0 <= u; //~ ERROR comparison is useless due to type limits } - -fn qux() { - let mut i = 1i8; - while 200 != i { //~ ERROR comparison is useless due to type limits - //~^ WARNING literal out of range for i8 - i += 1; - } -} diff --git a/src/test/compile-fail/lint-type-limits2.rs b/src/test/compile-fail/lint-type-limits2.rs new file mode 100644 index 0000000000000..20e756c803a9c --- /dev/null +++ b/src/test/compile-fail/lint-type-limits2.rs @@ -0,0 +1,23 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +// compile-flags: -D unused-comparisons +fn main() { } + + +fn bar() -> i8 { + return 123; +} + +fn baz() -> bool { + 128 > bar() //~ ERROR literal out of range for i8 +} diff --git a/src/test/compile-fail/lint-type-limits3.rs b/src/test/compile-fail/lint-type-limits3.rs new file mode 100644 index 0000000000000..fed4143a6d7a2 --- /dev/null +++ b/src/test/compile-fail/lint-type-limits3.rs @@ -0,0 +1,21 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +// compile-flags: -D unused-comparisons +fn main() { } + +fn qux() { + let mut i = 1i8; + while 200 != i { //~ ERROR literal out of range for i8 + i += 1; + } +} diff --git a/src/test/compile-fail/lint-type-overflow.rs b/src/test/compile-fail/lint-type-overflow.rs index eb5b77f7a45a3..ce336905c0139 100644 --- a/src/test/compile-fail/lint-type-overflow.rs +++ b/src/test/compile-fail/lint-type-overflow.rs @@ -25,7 +25,6 @@ fn main() { let x2: i8 = -128; // should be OK let x1: i8 = 128; //~ error: literal out of range for i8 - let x2: i8 = --128; //~ error: literal out of range for i8 let x3: i8 = -129; //~ error: literal out of range for i8 let x3: i8 = -(129); //~ error: literal out of range for i8 @@ -54,9 +53,4 @@ fn main() { let x = 18446744073709551615_i64; //~ error: literal out of range for i64 let x: i64 = -9223372036854775809; //~ error: literal out of range for i64 let x = -9223372036854775809_i64; //~ error: literal out of range for i64 - - let x = -3.40282348e+38_f32; //~ error: literal out of range for f32 - let x = 3.40282348e+38_f32; //~ error: literal out of range for f32 - let x = -1.7976931348623159e+308_f64; //~ error: literal out of range for f64 - let x = 1.7976931348623159e+308_f64; //~ error: literal out of range for f64 } diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs new file mode 100644 index 0000000000000..83300f18c3e95 --- /dev/null +++ b/src/test/compile-fail/lint-type-overflow2.rs @@ -0,0 +1,22 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// + +#![deny(overflowing_literals)] + +#[allow(unused_variables)] +fn main() { + let x2: i8 = --128; //~ error: literal out of range for i8 + + let x = -3.40282348e+38_f32; //~ error: literal out of range for f32 + let x = 3.40282348e+38_f32; //~ error: literal out of range for f32 + let x = -1.7976931348623159e+308_f64; //~ error: literal out of range for f64 + let x = 1.7976931348623159e+308_f64; //~ error: literal out of range for f64 +} diff --git a/src/test/compile-fail/repeat_count.rs b/src/test/compile-fail/repeat_count.rs index 9aa61418d6d04..c0913be1354ca 100644 --- a/src/test/compile-fail/repeat_count.rs +++ b/src/test/compile-fail/repeat_count.rs @@ -44,12 +44,12 @@ fn main() { //~^ ERROR mismatched types //~| expected `usize` //~| found `isize` - //~| ERROR expected positive integer for repeat count, found negative integer [E0306] + //~| ERROR expected positive integer for repeat count, found isize [E0306] let f = [0_usize; -1_isize]; //~^ ERROR mismatched types //~| expected `usize` //~| found `isize` - //~| ERROR expected positive integer for repeat count, found negative integer [E0306] + //~| ERROR expected positive integer for repeat count, found isize [E0306] struct G { g: (), } diff --git a/src/test/run-pass/const-fn.rs b/src/test/run-pass/const-fn.rs index 5961ed8d3390d..562040dc5620b 100644 --- a/src/test/run-pass/const-fn.rs +++ b/src/test/run-pass/const-fn.rs @@ -10,7 +10,7 @@ // A very basic test of const fn functionality. -#![feature(const_fn)] +#![feature(const_fn, const_indexing)] const fn add(x: u32, y: u32) -> u32 { x + y @@ -24,6 +24,14 @@ const unsafe fn div(x: u32, y: u32) -> u32 { x / y } +const fn generic(t: T) -> T { + t +} + +const fn generic_arr(t: [T; 1]) -> T { + t[0] +} + const SUM: u32 = add(44, 22); const DIFF: u32 = sub(44, 22); const DIV: u32 = unsafe{div(44, 22)}; @@ -36,4 +44,6 @@ fn main() { assert_eq!(DIV, 2); let _: [&'static str; sub(100, 99) as usize] = ["hi"]; + let _: [&'static str; generic(1)] = ["hi"]; + let _: [&'static str; generic_arr([1])] = ["hi"]; } diff --git a/src/test/run-pass/const-negation.rs b/src/test/run-pass/const-negation.rs new file mode 100644 index 0000000000000..afd82e7d8aced --- /dev/null +++ b/src/test/run-pass/const-negation.rs @@ -0,0 +1,37 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deny(const_err)] + +fn main() { + #[cfg(target_pointer_width = "32")] + const I: isize = -2147483648isize; + #[cfg(target_pointer_width = "64")] + const I: isize = -9223372036854775808isize; + assert_eq!(::std::i32::MIN as u64, 0xffffffff80000000); + assert_eq!(-2147483648isize as u64, 0xffffffff80000000); + assert_eq!(::std::i64::MIN as u64, 0x8000000000000000); + assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000); + const J: usize = ::std::i32::MAX as usize; + const K: usize = -1i32 as u32 as usize; + const L: usize = ::std::i32::MIN as usize; + const M: usize = ::std::i64::MIN as usize; + match 5 { + J => {}, + K => {}, + L => {}, + M => {}, + _ => {} + } + match 5 { + I => {}, + _ => {} + } +} diff --git a/src/test/run-pass/issue-23833.rs b/src/test/run-pass/issue-23833.rs new file mode 100644 index 0000000000000..7d63c41eb26e1 --- /dev/null +++ b/src/test/run-pass/issue-23833.rs @@ -0,0 +1,25 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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] + = [0; (i8::MAX as usize) - 1]; + +fn main() { + foo(&A_I8_T[..]); +} + +fn foo(x: T) { + println!("{:?}", x); +} diff --git a/src/test/run-pass/packed-struct-generic-layout.rs b/src/test/run-pass/packed-struct-generic-layout.rs index b962a5c793382..1c8a1322ad2a9 100644 --- a/src/test/run-pass/packed-struct-generic-layout.rs +++ b/src/test/run-pass/packed-struct-generic-layout.rs @@ -20,7 +20,7 @@ struct S { pub fn main() { unsafe { - let s = S { a: 0xff_ff_ff_ffu32, b: 1, c: 0xaa_aa_aa_aa as i32 }; + let s = S { a: 0xff_ff_ff_ffu32, b: 1, c: 0xaa_aa_aa_aa as u32 }; let transd : [u8; 9] = mem::transmute(s); // Don't worry about endianness, the numbers are palindromic. assert_eq!(transd, @@ -29,7 +29,7 @@ pub fn main() { 0xaa, 0xaa, 0xaa, 0xaa]); - let s = S { a: 1u8, b: 2u8, c: 0b10000001_10000001 as i16}; + let s = S { a: 1u8, b: 2u8, c: 0b10000001_10000001 as u16}; let transd : [u8; 4] = mem::transmute(s); // Again, no endianness problems. assert_eq!(transd, diff --git a/src/test/run-pass/shift.rs b/src/test/run-pass/shift.rs index 2f417e0e2f4e8..5ca6a02309927 100644 --- a/src/test/run-pass/shift.rs +++ b/src/test/run-pass/shift.rs @@ -45,7 +45,7 @@ fn test_expr() { let v4 = 4 as isize; let v2 = 2 as isize; assert_eq!(v10 >> v2 as usize, v2 as i8); - assert_eq!(v10 << v4 as usize, 160 as i8); + assert_eq!(v10 << v2 as usize, 40 as i8); let v10 = 10 as usize; let v4 = 4 as isize; @@ -71,9 +71,9 @@ fn test_const() { assert_eq!(r2_3, 160 as isize); static r1_4: i8 = 10i8 >> 2_usize; - static r2_4: i8 = 10i8 << 4_usize; + static r2_4: i8 = 10i8 << 2_usize; assert_eq!(r1_4, 2 as i8); - assert_eq!(r2_4, 160 as i8); + assert_eq!(r2_4, 40 as i8); static r1_5: usize = 10_usize >> 2_usize; static r2_5: usize = 10_usize << 4_usize; From 0d13231a4cfb1f24c7cfc8168d15d19c63bdbdba Mon Sep 17 00:00:00 2001 From: Oliver 'ker' Schneider Date: Sun, 27 Dec 2015 02:17:16 +0100 Subject: [PATCH 02/13] const eval failures aren't fatal, simply return a dummy instead --- src/librustc/middle/const_eval.rs | 16 ++++++++++++++-- src/librustc/middle/ty/mod.rs | 2 +- src/librustc_trans/trans/mir/constant.rs | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index c3f8d7bf9598e..bc8dfd6c7b016 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -267,6 +267,10 @@ pub enum ConstVal { Array(ast::NodeId, u64), Repeat(ast::NodeId, u64), Char(char), + /// A value that only occurs in case `eval_const_expr` reported an error. You should never + /// handle this case. Its sole purpose is to allow more errors to be reported instead of + /// causing a fatal error. + Dummy, } impl hash::Hash for ConstVal { @@ -283,6 +287,7 @@ impl hash::Hash for ConstVal { Array(a, n) => { a.hash(state); n.hash(state) }, Repeat(a, n) => { a.hash(state); n.hash(state) }, Char(c) => c.hash(state), + Dummy => ().hash(state), } } } @@ -305,6 +310,7 @@ impl PartialEq for ConstVal { (&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn), (&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn), (&Char(a), &Char(b)) => a == b, + (&Dummy, &Dummy) => true, // FIXME: should this be false? _ => false, } } @@ -326,6 +332,7 @@ impl ConstVal { Array(..) => "array", Repeat(..) => "repeat", Char(..) => "char", + Dummy => "dummy value", } } } @@ -393,7 +400,12 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P { pub fn eval_const_expr(tcx: &TyCtxt, e: &Expr) -> ConstVal { match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) { Ok(r) => r, - Err(s) => tcx.sess.span_fatal(s.span, &s.description()) + // non-const path still needs to be a fatal error, because enums are funky + Err(ref s) if s.kind == NonConstPath => tcx.sess.span_fatal(s.span, &s.description()), + Err(s) => { + tcx.sess.span_err(s.span, &s.description()); + Dummy + }, } } @@ -405,7 +417,7 @@ pub struct ConstEvalErr { pub kind: ErrKind, } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum ErrKind { CannotCast, CannotCastTo(&'static str), diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index c98ae2cceabaa..f8ab98a15d9c1 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -1546,7 +1546,7 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> { /// Asserts this is a struct and returns the struct's unique /// variant. pub fn struct_variant(&self) -> &VariantDefData<'tcx, 'container> { - assert!(self.adt_kind() == AdtKind::Struct); + assert_eq!(self.adt_kind(), AdtKind::Struct); &self.variants[0] } diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs index 2c251a4cd578c..c20d8b01eb773 100644 --- a/src/librustc_trans/trans/mir/constant.rs +++ b/src/librustc_trans/trans/mir/constant.rs @@ -93,6 +93,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }) }, ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false), + ConstVal::Dummy => unreachable!(), ConstVal::Function(_) => C_nil(ccx) } } From 41f11d95c70cc2f65ee9f068d46a7a6de8f911b3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 24 Feb 2016 16:07:31 +0100 Subject: [PATCH 03/13] don't guess const fn argument types --- src/librustc/middle/const_eval.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index bc8dfd6c7b016..993c59a5f2c68 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -854,24 +854,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, let mut call_args = NodeMap(); for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) { - let arg_hint = match ty_hint { - ExprTypeChecked => ExprTypeChecked, - UncheckedExprNoHint | UncheckedExprHint(_) => { - if let Some(hint) = tcx.ast_ty_to_ty_cache.borrow().get(&arg.ty.id) { - let mut new_ty_hint = UncheckedExprHint(hint); - for t in hint.walk() { - if let ty::TypeVariants::TyParam(_) = t.sty { - // found a generic argument, but we are in typeck - new_ty_hint = UncheckedExprNoHint; - break; - } - } - new_ty_hint - } else { - UncheckedExprNoHint - } - }, - }; + let arg_hint = ty_hint.erase_hint(); let arg_val = try!(eval_const_expr_partial( tcx, arg_expr, From 6ee60037f9cee27d9de70195d7e63d4946465a2f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Feb 2016 10:13:36 +0100 Subject: [PATCH 04/13] infer integral types in presence of a type hint --- src/librustc/middle/const_eval.rs | 114 ++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 993c59a5f2c68..66ec1d691490e 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -36,6 +36,7 @@ use syntax::codemap::Span; use syntax::parse::token::InternedString; use syntax::ptr::P; use syntax::codemap; +use syntax::attr::IntType; use std::borrow::Cow; use std::cmp::Ordering; @@ -462,6 +463,10 @@ pub enum ErrKind { IndexOpFeatureGated, Math(ConstMathErr), + + IntermediateUnsignedNegative, + InferredWrongType(ConstInt), + BadType(ConstVal), } impl From for ErrKind { @@ -518,6 +523,13 @@ impl ConstEvalErr { MiscCatchAll => "unsupported constant expr".into_cow(), IndexOpFeatureGated => "the index operation on const values is unstable".into_cow(), Math(ref err) => err.description().into_cow(), + + IntermediateUnsignedNegative => "during the computation of an unsigned a negative \ + number was encountered. This is most likely a bug in\ + the constant evaluator".into_cow(), + + InferredWrongType(ref i) => format!("inferred wrong type for {}", i).into_cow(), + BadType(ref i) => format!("value of wrong type: {:?}", i).into_cow(), } } } @@ -974,7 +986,101 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, _ => signal!(e, MiscCatchAll) }; - Ok(result) + match (ety.map(|t| &t.sty), result) { + (Some(ref ty_hint), Integral(i)) => Ok(Integral(try!(infer(i, tcx, ty_hint, e.span)))), + (_, result) => Ok(result), + } +} + +fn infer<'tcx>( + i: ConstInt, + tcx: &TyCtxt<'tcx>, + ty_hint: &ty::TypeVariants<'tcx>, + span: Span +) -> Result { + use syntax::ast::*; + const I8MAX: u64 = ::std::i8::MAX as u64; + const I16MAX: u64 = ::std::i16::MAX as u64; + const I32MAX: u64 = ::std::i32::MAX as u64; + const I64MAX: u64 = ::std::i64::MAX as u64; + + const U8MAX: u64 = ::std::u8::MAX as u64; + const U16MAX: u64 = ::std::u16::MAX as u64; + const U32MAX: u64 = ::std::u32::MAX as u64; + + const I8MAXI: i64 = ::std::i8::MAX as i64; + const I16MAXI: i64 = ::std::i16::MAX as i64; + const I32MAXI: i64 = ::std::i32::MAX as i64; + + const I8MINI: i64 = ::std::i8::MIN as i64; + const I16MINI: i64 = ::std::i16::MIN as i64; + const I32MINI: i64 = ::std::i32::MIN as i64; + + let err = |e| ConstEvalErr { + span: span, + kind: e, + }; + + match (ty_hint, i) { + (&ty::TyInt(IntTy::I8), result @ I8(_)) => Ok(result), + (&ty::TyInt(IntTy::I16), result @ I16(_)) => Ok(result), + (&ty::TyInt(IntTy::I32), result @ I32(_)) => Ok(result), + (&ty::TyInt(IntTy::I64), result @ I64(_)) => Ok(result), + (&ty::TyInt(IntTy::Is), result @ Isize(_)) => Ok(result), + + (&ty::TyUint(UintTy::U8), result @ U8(_)) => Ok(result), + (&ty::TyUint(UintTy::U16), result @ U16(_)) => Ok(result), + (&ty::TyUint(UintTy::U32), result @ U32(_)) => Ok(result), + (&ty::TyUint(UintTy::U64), result @ U64(_)) => Ok(result), + (&ty::TyUint(UintTy::Us), result @ Usize(_)) => Ok(result), + + (&ty::TyInt(IntTy::I8), Infer(i @ 0...I8MAX)) => Ok(I8(i as i8)), + (&ty::TyInt(IntTy::I16), Infer(i @ 0...I16MAX)) => Ok(I16(i as i16)), + (&ty::TyInt(IntTy::I32), Infer(i @ 0...I32MAX)) => Ok(I32(i as i32)), + (&ty::TyInt(IntTy::I64), Infer(i @ 0...I64MAX)) => Ok(I64(i as i64)), + (&ty::TyInt(IntTy::Is), Infer(i @ 0...I64MAX)) => { + match ConstIsize::new(i as i64, tcx.sess.target.int_type) { + Ok(val) => Ok(Isize(val)), + Err(e) => Err(err(e.into())), + } + }, + (&ty::TyInt(_), Infer(_)) => Err(err(Math(ConstMathErr::NotInRange))), + + (&ty::TyInt(IntTy::I8), InferSigned(i @ I8MINI...I8MAXI)) => Ok(I8(i as i8)), + (&ty::TyInt(IntTy::I16), InferSigned(i @ I16MINI...I16MAXI)) => Ok(I16(i as i16)), + (&ty::TyInt(IntTy::I32), InferSigned(i @ I32MINI...I32MAXI)) => Ok(I32(i as i32)), + (&ty::TyInt(IntTy::I64), InferSigned(i)) => Ok(I64(i)), + (&ty::TyInt(IntTy::Is), InferSigned(i)) => { + match ConstIsize::new(i, tcx.sess.target.int_type) { + Ok(val) => Ok(Isize(val)), + Err(e) => Err(err(e.into())), + } + }, + (&ty::TyInt(_), InferSigned(_)) => Err(err(Math(ConstMathErr::NotInRange))), + + (&ty::TyUint(UintTy::U8), Infer(i @ 0...U8MAX)) => Ok(U8(i as u8)), + (&ty::TyUint(UintTy::U16), Infer(i @ 0...U16MAX)) => Ok(U16(i as u16)), + (&ty::TyUint(UintTy::U32), Infer(i @ 0...U32MAX)) => Ok(U32(i as u32)), + (&ty::TyUint(UintTy::U64), Infer(i)) => Ok(U64(i)), + (&ty::TyUint(UintTy::Us), Infer(i)) => { + match ConstUsize::new(i, tcx.sess.target.uint_type) { + Ok(val) => Ok(Usize(val)), + Err(e) => Err(err(e.into())), + } + }, + (&ty::TyUint(_), Infer(_)) => Err(err(Math(ConstMathErr::NotInRange))), + (&ty::TyUint(_), InferSigned(_)) => Err(err(IntermediateUnsignedNegative)), + + (&ty::TyInt(_), i) | + (&ty::TyUint(_), i) => Err(err(InferredWrongType(i))), + + (&ty::TyEnum(ref adt, _), i) => { + let hints = tcx.lookup_repr_hints(adt.did); + let int_ty = tcx.enum_repr_type(hints.iter().next()); + infer(i, tcx, &int_ty.to_ty(tcx).sty, span) + }, + (_, i) => Err(err(BadType(ConstVal::Integral(i)))), + } } fn impl_or_trait_container(tcx: &TyCtxt, def_id: DefId) -> ty::ImplOrTraitItemContainer { @@ -1083,7 +1189,7 @@ fn cast_const_int<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstInt, ty: ty::Ty) -> CastRe } } -fn cast_const_float<'tcx>(tcx: &ty::ctxt<'tcx>, f: f64, ty: ty::Ty) -> CastResult { +fn cast_const_float<'tcx>(tcx: &TyCtxt<'tcx>, f: f64, ty: ty::Ty) -> CastResult { match ty.sty { ty::TyInt(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty), ty::TyInt(_) => cast_const_int(tcx, InferSigned(f as i64), ty), @@ -1094,7 +1200,7 @@ fn cast_const_float<'tcx>(tcx: &ty::ctxt<'tcx>, f: f64, ty: ty::Ty) -> CastResul } } -fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: ConstVal, ty: ty::Ty) -> CastResult { +fn cast_const<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstVal, ty: ty::Ty) -> CastResult { match val { Integral(i) => cast_const_int(tcx, i, ty), Bool(b) => cast_const_int(tcx, Infer(b as u64), ty), @@ -1105,7 +1211,7 @@ fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: ConstVal, ty: ty::Ty) -> CastResu } fn lit_to_const<'tcx>(lit: &ast::LitKind, - tcx: &ty::ctxt<'tcx>, + tcx: &TyCtxt<'tcx>, ty_hint: Option>, span: Span, ) -> Result { From 54b15c7160e165621f0a7ae6a71db30dded254df Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Feb 2016 10:14:10 +0100 Subject: [PATCH 05/13] the type hint given during a cast operation is just a soft hint --- src/librustc/middle/const_eval.rs | 16 +++++++++++++++- src/librustc_const_eval/int.rs | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 66ec1d691490e..f4c5196d0cdb5 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -743,7 +743,21 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, } }; - let val = try!(eval_const_expr_partial(tcx, &base, base_hint, fn_args)); + let val = match eval_const_expr_partial(tcx, &base, base_hint, fn_args) { + Ok(val) => val, + Err(ConstEvalErr { kind: InferredWrongType(val), .. }) => { + // Something like `5i8 as usize` doesn't need a type hint for the base + // instead take the type hint from the inner value + let hint = match val.int_type() { + Some(IntType::UnsignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_uint(ty)), + Some(IntType::SignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_int(ty)), + // we had a type hint, so we can't have an unknown type + None => unreachable!(), + }; + try!(eval_const_expr_partial(tcx, &base, hint, fn_args)) + }, + Err(e) => return Err(e), + }; match cast_const(tcx, val, ety) { Ok(val) => val, Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }), diff --git a/src/librustc_const_eval/int.rs b/src/librustc_const_eval/int.rs index 896d4a9b7d5e2..bb72711379eaa 100644 --- a/src/librustc_const_eval/int.rs +++ b/src/librustc_const_eval/int.rs @@ -9,6 +9,8 @@ // except according to those terms. use std::cmp::Ordering; +use syntax::attr::IntType; +use syntax::ast::{IntTy, UintTy}; use super::is::*; use super::us::*; @@ -258,6 +260,22 @@ impl ConstInt { ConstInt::Infer(_) | ConstInt::InferSigned(_) => panic!("no type info for const int"), } } + + pub fn int_type(self) -> Option { + match self { + ConstInt::I8(_) => Some(IntType::SignedInt(IntTy::I8)), + ConstInt::I16(_) => Some(IntType::SignedInt(IntTy::I16)), + ConstInt::I32(_) => Some(IntType::SignedInt(IntTy::I32)), + ConstInt::I64(_) => Some(IntType::SignedInt(IntTy::I64)), + ConstInt::Isize(_) => Some(IntType::SignedInt(IntTy::Is)), + ConstInt::U8(_) => Some(IntType::UnsignedInt(UintTy::U8)), + ConstInt::U16(_) => Some(IntType::UnsignedInt(UintTy::U16)), + ConstInt::U32(_) => Some(IntType::UnsignedInt(UintTy::U32)), + ConstInt::U64(_) => Some(IntType::UnsignedInt(UintTy::U64)), + ConstInt::Usize(_) => Some(IntType::UnsignedInt(UintTy::Us)), + _ => None, + } + } } impl ::std::cmp::PartialOrd for ConstInt { From bba1596c71989bdbad19ec06b27fa0d81d5bc58f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 25 Feb 2016 11:12:18 +0100 Subject: [PATCH 06/13] also print the expected type in the error message --- src/librustc/middle/const_eval.rs | 14 +++++++++----- src/test/compile-fail/const-eval-overflow-3.rs | 2 +- src/test/compile-fail/const-eval-overflow-4b.rs | 4 +++- src/test/compile-fail/issue-8761.rs | 8 ++++++-- src/test/compile-fail/repeat_count.rs | 12 ++++++++---- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index f4c5196d0cdb5..f56c42738e71e 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -465,7 +465,8 @@ pub enum ErrKind { Math(ConstMathErr), IntermediateUnsignedNegative, - InferredWrongType(ConstInt), + /// Expected, Got + TypeMismatch(String, ConstInt), BadType(ConstVal), } @@ -528,7 +529,10 @@ impl ConstEvalErr { number was encountered. This is most likely a bug in\ the constant evaluator".into_cow(), - InferredWrongType(ref i) => format!("inferred wrong type for {}", i).into_cow(), + TypeMismatch(ref expected, ref got) => { + format!("mismatched types: expected `{}`, found `{}`", + expected, got.description()).into_cow() + }, BadType(ref i) => format!("value of wrong type: {:?}", i).into_cow(), } } @@ -745,7 +749,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, let val = match eval_const_expr_partial(tcx, &base, base_hint, fn_args) { Ok(val) => val, - Err(ConstEvalErr { kind: InferredWrongType(val), .. }) => { + Err(ConstEvalErr { kind: TypeMismatch(_, val), .. }) => { // Something like `5i8 as usize` doesn't need a type hint for the base // instead take the type hint from the inner value let hint = match val.int_type() { @@ -1085,8 +1089,8 @@ fn infer<'tcx>( (&ty::TyUint(_), Infer(_)) => Err(err(Math(ConstMathErr::NotInRange))), (&ty::TyUint(_), InferSigned(_)) => Err(err(IntermediateUnsignedNegative)), - (&ty::TyInt(_), i) | - (&ty::TyUint(_), i) => Err(err(InferredWrongType(i))), + (&ty::TyInt(ity), i) => Err(err(TypeMismatch(ity.to_string(), i))), + (&ty::TyUint(ity), i) => Err(err(TypeMismatch(ity.to_string(), i))), (&ty::TyEnum(ref adt, _), i) => { let hints = tcx.lookup_repr_hints(adt.did); diff --git a/src/test/compile-fail/const-eval-overflow-3.rs b/src/test/compile-fail/const-eval-overflow-3.rs index 33fa4aae61151..c90ae045f96b4 100644 --- a/src/test/compile-fail/const-eval-overflow-3.rs +++ b/src/test/compile-fail/const-eval-overflow-3.rs @@ -17,7 +17,7 @@ // self-hosted and a cross-compiled setup; therefore resorting to // error-pattern for now. -// error-pattern: expected constant integer for repeat count, but tried to add two integrals of +// error-pattern: expected constant integer for repeat count, but attempted to add with overflow #![allow(unused_imports)] diff --git a/src/test/compile-fail/const-eval-overflow-4b.rs b/src/test/compile-fail/const-eval-overflow-4b.rs index 68ef1b4775189..5aa93cf6383fe 100644 --- a/src/test/compile-fail/const-eval-overflow-4b.rs +++ b/src/test/compile-fail/const-eval-overflow-4b.rs @@ -21,7 +21,9 @@ use std::{u8, u16, u32, u64, usize}; const A_I8_T : [u32; (i8::MAX as i8 + 1u8) as usize] - //~^ ERROR tried to add two integrals of different types [E0250] + //~^ ERROR mismatched types: + //~| expected `i8`, + //~| found `u8` [E0250] = [0; (i8::MAX as usize) + 1]; fn main() { diff --git a/src/test/compile-fail/issue-8761.rs b/src/test/compile-fail/issue-8761.rs index da8bd1dc28d98..1c98abce0304e 100644 --- a/src/test/compile-fail/issue-8761.rs +++ b/src/test/compile-fail/issue-8761.rs @@ -10,9 +10,13 @@ enum Foo { A = 1i64, - //~^ ERROR mismatched types: expected `isize` got `i64` + //~^ ERROR mismatched types: + //~| expected `isize`, + //~| found `i64` [E0080] B = 2u8 - //~^ ERROR mismatched types: expected `isize` got `u8` + //~^ ERROR mismatched types: + //~| expected `isize`, + //~| found `u8` [E0080] } fn main() {} diff --git a/src/test/compile-fail/repeat_count.rs b/src/test/compile-fail/repeat_count.rs index c0913be1354ca..10b722946a8a8 100644 --- a/src/test/compile-fail/repeat_count.rs +++ b/src/test/compile-fail/repeat_count.rs @@ -43,13 +43,17 @@ fn main() { let f = [0; -4_isize]; //~^ ERROR mismatched types //~| expected `usize` - //~| found `isize` - //~| ERROR expected positive integer for repeat count, found isize [E0306] + //~| found `isize` [E0308] + //~| ERROR mismatched types: + //~| expected `usize`, + //~| found `isize` [E0307] let f = [0_usize; -1_isize]; //~^ ERROR mismatched types //~| expected `usize` - //~| found `isize` - //~| ERROR expected positive integer for repeat count, found isize [E0306] + //~| found `isize` [E0308] + //~| ERROR mismatched types + //~| expected `usize` + //~| found `isize` [E0307] struct G { g: (), } From a48bd1718356cdcc4204b57ed282ff8111df5427 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Mar 2016 14:46:20 +0100 Subject: [PATCH 07/13] prefer the (associated) const's type over the type hint --- src/librustc/middle/const_eval.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index f56c42738e71e..c02782b859cbc 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -854,16 +854,12 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, Some(actual_e) => actual_e, None => signal!(e, NonConstPath) }; - let item_hint = if let UncheckedExprNoHint = ty_hint { - match const_ty { - Some(ty) => match ast_ty_to_prim_ty(tcx, ty) { - Some(ty) => UncheckedExprHint(ty), - None => UncheckedExprNoHint - }, - None => UncheckedExprNoHint - } - } else { - ty_hint + let item_hint = match const_ty { + Some(ty) => match ast_ty_to_prim_ty(tcx, ty) { + Some(ty) => ty_hint.checked_or(ty), + None => ty_hint.erase_hint(), + }, + None => ty_hint.erase_hint(), }; try!(eval_const_expr_partial(tcx, const_expr, item_hint, fn_args)) } From 6992280f0003e825ad05daf6b5aed94c63e77773 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 3 Mar 2016 14:48:08 +0100 Subject: [PATCH 08/13] simplify const path lookup for constants and associated constants --- src/librustc/middle/check_match.rs | 2 +- src/librustc/middle/const_eval.rs | 164 ++++++++++------------------ src/librustc/middle/ty/context.rs | 8 +- src/librustc_mir/hair/cx/pattern.rs | 2 +- src/librustc_passes/consts.rs | 2 +- src/librustc_trans/trans/consts.rs | 2 +- 6 files changed, 65 insertions(+), 115 deletions(-) diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 1f79e76f3ac32..51985be96db53 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -476,7 +476,7 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> { Some(Def::AssociatedConst(did)) | Some(Def::Const(did)) => match lookup_const_by_id(self.tcx, did, Some(pat.id), None) { - Some(const_expr) => { + Some((const_expr, _const_ty)) => { const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| { if let Some(ref mut renaming_map) = self.renaming_map { diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index c02782b859cbc..3690f0c1d664b 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -98,13 +98,13 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, def_id: DefId, maybe_ref_id: Option, param_substs: Option<&'tcx subst::Substs<'tcx>>) - -> Option<&'tcx Expr> { + -> Option<(&'tcx Expr, Option>)> { if let Some(node_id) = tcx.map.as_local_node_id(def_id) { match tcx.map.find(node_id) { None => None, Some(ast_map::NodeItem(it)) => match it.node { - hir::ItemConst(_, ref const_expr) => { - Some(&const_expr) + hir::ItemConst(ref ty, ref const_expr) => { + Some((&const_expr, ast_ty_to_prim_ty(tcx, ty))) } _ => None }, @@ -123,8 +123,7 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, if let Some(param_substs) = param_substs { substs = substs.subst(tcx, param_substs); } - resolve_trait_associated_const(tcx, ti, trait_id, - substs) + resolve_trait_associated_const(tcx, ti, trait_id, substs) } // Technically, without knowing anything about the // expression that generates the obligation, we could @@ -138,8 +137,8 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, _ => None }, Some(ast_map::NodeImplItem(ii)) => match ii.node { - hir::ImplItemKind::Const(_, ref expr) => { - Some(&expr) + hir::ImplItemKind::Const(ref ty, ref expr) => { + Some((&expr, ast_ty_to_prim_ty(tcx, ty))) } _ => None }, @@ -147,16 +146,18 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, } } else { match tcx.extern_const_statics.borrow().get(&def_id) { - Some(&ast::DUMMY_NODE_ID) => return None, - Some(&expr_id) => { - return Some(tcx.map.expect_expr(expr_id)); + Some(&None) => return None, + Some(&Some((expr_id, ty))) => { + return Some((tcx.map.expect_expr(expr_id), ty)); } None => {} } let mut used_ref_id = false; - let expr_id = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { + let expr_ty = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => match item.node { - hir::ItemConst(_, ref const_expr) => Some(const_expr.id), + hir::ItemConst(ref ty, ref const_expr) => { + Some((&**const_expr, ast_ty_to_prim_ty(tcx, ty))) + }, _ => None }, cstore::FoundAst::Found(&InlinedItem::TraitItem(trait_id, ref ti)) => match ti.node { @@ -173,8 +174,7 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, if let Some(param_substs) = param_substs { substs = substs.subst(tcx, param_substs); } - resolve_trait_associated_const(tcx, ti, trait_id, - substs).map(|e| e.id) + resolve_trait_associated_const(tcx, ti, trait_id, substs) } None => None } @@ -182,7 +182,9 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, _ => None }, cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref ii)) => match ii.node { - hir::ImplItemKind::Const(_, ref expr) => Some(expr.id), + hir::ImplItemKind::Const(ref ty, ref expr) => { + Some((&**expr, ast_ty_to_prim_ty(tcx, ty))) + }, _ => None }, _ => None @@ -192,10 +194,10 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, // lookup with the same def_id may yield a different result. if !used_ref_id { tcx.extern_const_statics - .borrow_mut().insert(def_id, - expr_id.unwrap_or(ast::DUMMY_NODE_ID)); + .borrow_mut() + .insert(def_id, expr_ty.map(|(e, t)| (e.id, t))); } - expr_id.map(|id| tcx.map.expect_expr(id)) + expr_ty } } @@ -386,7 +388,7 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P { PatKind::Path(path.clone()), Some(Def::Const(def_id)) | Some(Def::AssociatedConst(def_id)) => { - let expr = lookup_const_by_id(tcx, def_id, Some(expr.id), None).unwrap(); + let (expr, _ty) = lookup_const_by_id(tcx, def_id, Some(expr.id), None).unwrap(); return const_expr_to_pat(tcx, expr, span); }, _ => unreachable!(), @@ -778,90 +780,49 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, if def.depth != 0 { signal!(e, UnresolvedPath); } - Some(def.full_def()) + def.full_def() } else { - None + signal!(e, NonConstPath); }; - let (const_expr, const_ty) = match opt_def { - Some(Def::Const(def_id)) => { - if let Some(node_id) = tcx.map.as_local_node_id(def_id) { - match tcx.map.find(node_id) { - Some(ast_map::NodeItem(it)) => match it.node { - hir::ItemConst(ref ty, ref expr) => { - (Some(&**expr), Some(&**ty)) - } - _ => (None, None) - }, - _ => (None, None) - } + match opt_def { + Def::Const(def_id) | + Def::AssociatedConst(def_id) => { + let maybe_ref_id = if let ExprTypeChecked = ty_hint { + Some(e.id) + } else { + None + }; + if let Some((e, ty)) = lookup_const_by_id(tcx, def_id, maybe_ref_id, None) { + let item_hint = match ty { + Some(ty) => ty_hint.checked_or(ty), + None => ty_hint, + }; + try!(eval_const_expr_partial(tcx, e, item_hint, None)) } else { - (lookup_const_by_id(tcx, def_id, Some(e.id), None), None) + signal!(e, NonConstPath); } - } - Some(Def::AssociatedConst(def_id)) => { - if let Some(node_id) = tcx.map.as_local_node_id(def_id) { - match impl_or_trait_container(tcx, def_id) { - ty::TraitContainer(trait_id) => match tcx.map.find(node_id) { - Some(ast_map::NodeTraitItem(ti)) => match ti.node { - hir::ConstTraitItem(ref ty, _) => { - if let ExprTypeChecked = ty_hint { - let substs = tcx.node_id_item_substs(e.id).substs; - (resolve_trait_associated_const(tcx, - ti, - trait_id, - substs), - Some(&**ty)) - } else { - (None, None) - } - } - _ => (None, None) - }, - _ => (None, None) - }, - ty::ImplContainer(_) => match tcx.map.find(node_id) { - Some(ast_map::NodeImplItem(ii)) => match ii.node { - hir::ImplItemKind::Const(ref ty, ref expr) => { - (Some(&**expr), Some(&**ty)) - } - _ => (None, None) - }, - _ => (None, None) - }, - } + }, + Def::Variant(enum_def, variant_def) => { + if let Some(const_expr) = lookup_variant_by_id(tcx, enum_def, variant_def) { + try!(eval_const_expr_partial(tcx, const_expr, ty_hint, None)) } else { - (lookup_const_by_id(tcx, def_id, Some(e.id), None), None) + signal!(e, NonConstPath); } } - Some(Def::Variant(enum_def, variant_def)) => { - (lookup_variant_by_id(tcx, enum_def, variant_def), None) + Def::Struct(..) => { + ConstVal::Struct(e.id) } - Some(Def::Struct(..)) => { - return Ok(ConstVal::Struct(e.id)) - } - Some(Def::Local(_, id)) => { + Def::Local(_, id) => { debug!("Def::Local({:?}): {:?}", id, fn_args); if let Some(val) = fn_args.and_then(|args| args.get(&id)) { - return Ok(val.clone()); + val.clone() } else { - (None, None) + signal!(e, NonConstPath); } }, - Some(Def::Method(id)) | Some(Def::Fn(id)) => return Ok(Function(id)), - _ => (None, None) - }; - let const_expr = match const_expr { - Some(actual_e) => actual_e, - None => signal!(e, NonConstPath) - }; - let item_hint = match const_ty { - Some(ty) => match ast_ty_to_prim_ty(tcx, ty) { - Some(ty) => ty_hint.checked_or(ty), - None => ty_hint.erase_hint(), - }, - None => ty_hint.erase_hint(), - }; - try!(eval_const_expr_partial(tcx, const_expr, item_hint, fn_args)) + Def::Method(id) | Def::Fn(id) => Function(id), + _ => signal!(e, NonConstPath), + } } hir::ExprCall(ref callee, ref args) => { let sub_ty_hint = ty_hint.erase_hint(); @@ -1097,28 +1058,11 @@ fn infer<'tcx>( } } -fn impl_or_trait_container(tcx: &TyCtxt, def_id: DefId) -> ty::ImplOrTraitItemContainer { - // This is intended to be equivalent to tcx.impl_or_trait_item(def_id).container() - // for local def_id, but it can be called before tcx.impl_or_trait_items is complete. - if let Some(node_id) = tcx.map.as_local_node_id(def_id) { - if let Some(ast_map::NodeItem(item)) = tcx.map.find(tcx.map.get_parent_node(node_id)) { - let container_id = tcx.map.local_def_id(item.id); - match item.node { - hir::ItemImpl(..) => return ty::ImplContainer(container_id), - hir::ItemTrait(..) => return ty::TraitContainer(container_id), - _ => () - } - } - panic!("No impl or trait container for {:?}", def_id); - } - panic!("{:?} is not local", def_id); -} - fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, ti: &'tcx hir::TraitItem, trait_id: DefId, rcvr_substs: subst::Substs<'tcx>) - -> Option<&'tcx Expr> + -> Option<(&'tcx Expr, Option>)> { let trait_ref = ty::Binder( rcvr_substs.erase_regions().to_trait_ref(tcx, trait_id) @@ -1151,7 +1095,9 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a TyCtxt<'tcx>, .iter().find(|ic| ic.name == ti.name) { Some(ic) => lookup_const_by_id(tcx, ic.def_id, None, None), None => match ti.node { - hir::ConstTraitItem(_, Some(ref expr)) => Some(&*expr), + hir::ConstTraitItem(ref ty, Some(ref expr)) => { + Some((&*expr, ast_ty_to_prim_ty(tcx, ty))) + }, _ => None, }, } diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index 61a7f49f45d4e..03c13115aea62 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -342,8 +342,12 @@ pub struct TyCtxt<'tcx> { /// FIXME(arielb1): why is this separate from populated_external_types? pub populated_external_primitive_impls: RefCell, - /// These caches are used by const_eval when decoding external constants. - pub extern_const_statics: RefCell>, + /// Cache used by const_eval when decoding external constants. + /// Contains `None` when the constant has been fetched but doesn't exist. + /// Constains `Some(expr_id, type)` otherwise. + /// `type` is `None` in case it's not a primitive type + pub extern_const_statics: RefCell>)>>>, + /// Cache used by const_eval when decoding extern const fns pub extern_const_fns: RefCell>, pub node_lint_levels: RefCell PatCx<'patcx, 'cx, 'tcx> { Def::Const(def_id) | Def::AssociatedConst(def_id) => match const_eval::lookup_const_by_id(self.cx.tcx, def_id, Some(pat.id), None) { - Some(const_expr) => { + Some((const_expr, _const_ty)) => { let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr, pat.span); return self.to_pattern(&pat); diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 3a39a3c6dd194..a9e03bba7db8e 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -604,7 +604,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } Some(Def::Const(did)) | Some(Def::AssociatedConst(did)) => { - if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did, + if let Some((expr, _ty)) = const_eval::lookup_const_by_id(v.tcx, did, Some(e.id), None) { let inner = v.global_expr(Mode::Const, expr); diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index ebe19aa972ce5..7d37627ad0eea 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -227,7 +227,7 @@ pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } match const_eval::lookup_const_by_id(ccx.tcx(), def_id, Some(ref_expr.id), Some(param_substs)) { - Some(ref expr) => expr, + Some((ref expr, _ty)) => expr, None => { ccx.sess().span_bug(ref_expr.span, "constant item not found") } From 7003ed3fa75f714bd636392ad4c40968652f6320 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 4 Mar 2016 12:33:23 +0100 Subject: [PATCH 09/13] fix rustdoc --- src/librustdoc/clean/inline.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index c14e4af810310..ccc2c78c3f6e0 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -320,7 +320,7 @@ pub fn build_impl(cx: &DocContext, let type_scheme = tcx.lookup_item_type(did); let default = if assoc_const.has_value { Some(const_eval::lookup_const_by_id(tcx, did, None, None) - .unwrap().span.to_src(cx)) + .unwrap().0.span.to_src(cx)) } else { None }; @@ -462,7 +462,7 @@ fn build_const(cx: &DocContext, tcx: &TyCtxt, use rustc::middle::const_eval; use rustc_front::print::pprust; - let expr = const_eval::lookup_const_by_id(tcx, did, None, None).unwrap_or_else(|| { + let (expr, ty) = const_eval::lookup_const_by_id(tcx, did, None, None).unwrap_or_else(|| { panic!("expected lookup_const_by_id to succeed for {:?}", did); }); debug!("converting constant expr {:?} to snippet", expr); @@ -470,7 +470,7 @@ fn build_const(cx: &DocContext, tcx: &TyCtxt, debug!("got snippet {}", sn); clean::Constant { - type_: tcx.lookup_item_type(did).ty.clean(cx), + type_: ty.map(|t| t.clean(cx)).unwrap_or_else(|| tcx.lookup_item_type(did).ty.clean(cx)), expr: sn } } From d4d6260ef1f21668194d4b3423d4b0b155e0b08e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 9 Mar 2016 19:27:36 +0100 Subject: [PATCH 10/13] don't be a breaking change, even in presence of overflowing literals --- src/librustc/middle/const_eval.rs | 110 ++++++------------ src/librustc_const_eval/int.rs | 8 +- .../compile-fail/enum-discrim-too-small2.rs | 11 +- src/test/compile-fail/lint-type-limits2.rs | 3 +- src/test/compile-fail/lint-type-limits3.rs | 3 +- src/test/run-pass/const-negation.rs | 5 + 6 files changed, 57 insertions(+), 83 deletions(-) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 3690f0c1d664b..d97df585edc05 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -855,10 +855,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, debug!("const call({:?})", call_args); try!(eval_const_expr_partial(tcx, &result, ty_hint, Some(&call_args))) }, - hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ety, lit.span) { - Ok(val) => val, - Err(err) => signal!(e, Math(err)), - }, + hir::ExprLit(ref lit) => try!(lit_to_const(&lit.node, tcx, ety, lit.span)), hir::ExprBlock(ref block) => { match block.expr { Some(ref expr) => try!(eval_const_expr_partial(tcx, &expr, ty_hint, fn_args)), @@ -926,7 +923,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, if let Tuple(tup_id) = c { if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node { if index.node < fields.len() { - return eval_const_expr_partial(tcx, &fields[index.node], ty_hint, fn_args) + try!(eval_const_expr_partial(tcx, &fields[index.node], ty_hint, fn_args)) } else { signal!(e, TupleIndexOutOfBounds); } @@ -947,7 +944,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, // if the idents are compared run-pass/issue-19244 fails if let Some(f) = fields.iter().find(|f| f.name.node == field_name.node) { - return eval_const_expr_partial(tcx, &f.expr, ty_hint, fn_args) + try!(eval_const_expr_partial(tcx, &f.expr, ty_hint, fn_args)) } else { signal!(e, MissingStructField); } @@ -974,22 +971,6 @@ fn infer<'tcx>( span: Span ) -> Result { use syntax::ast::*; - const I8MAX: u64 = ::std::i8::MAX as u64; - const I16MAX: u64 = ::std::i16::MAX as u64; - const I32MAX: u64 = ::std::i32::MAX as u64; - const I64MAX: u64 = ::std::i64::MAX as u64; - - const U8MAX: u64 = ::std::u8::MAX as u64; - const U16MAX: u64 = ::std::u16::MAX as u64; - const U32MAX: u64 = ::std::u32::MAX as u64; - - const I8MAXI: i64 = ::std::i8::MAX as i64; - const I16MAXI: i64 = ::std::i16::MAX as i64; - const I32MAXI: i64 = ::std::i32::MAX as i64; - - const I8MINI: i64 = ::std::i8::MIN as i64; - const I16MINI: i64 = ::std::i16::MIN as i64; - const I32MINI: i64 = ::std::i32::MIN as i64; let err = |e| ConstEvalErr { span: span, @@ -1009,41 +990,38 @@ fn infer<'tcx>( (&ty::TyUint(UintTy::U64), result @ U64(_)) => Ok(result), (&ty::TyUint(UintTy::Us), result @ Usize(_)) => Ok(result), - (&ty::TyInt(IntTy::I8), Infer(i @ 0...I8MAX)) => Ok(I8(i as i8)), - (&ty::TyInt(IntTy::I16), Infer(i @ 0...I16MAX)) => Ok(I16(i as i16)), - (&ty::TyInt(IntTy::I32), Infer(i @ 0...I32MAX)) => Ok(I32(i as i32)), - (&ty::TyInt(IntTy::I64), Infer(i @ 0...I64MAX)) => Ok(I64(i as i64)), - (&ty::TyInt(IntTy::Is), Infer(i @ 0...I64MAX)) => { + (&ty::TyInt(IntTy::I8), Infer(i)) => Ok(I8(i as i64 as i8)), + (&ty::TyInt(IntTy::I16), Infer(i)) => Ok(I16(i as i64 as i16)), + (&ty::TyInt(IntTy::I32), Infer(i)) => Ok(I32(i as i64 as i32)), + (&ty::TyInt(IntTy::I64), Infer(i)) => Ok(I64(i as i64)), + (&ty::TyInt(IntTy::Is), Infer(i)) => { match ConstIsize::new(i as i64, tcx.sess.target.int_type) { Ok(val) => Ok(Isize(val)), - Err(e) => Err(err(e.into())), + Err(_) => Ok(Isize(ConstIsize::Is32(i as i64 as i32))), } }, - (&ty::TyInt(_), Infer(_)) => Err(err(Math(ConstMathErr::NotInRange))), - (&ty::TyInt(IntTy::I8), InferSigned(i @ I8MINI...I8MAXI)) => Ok(I8(i as i8)), - (&ty::TyInt(IntTy::I16), InferSigned(i @ I16MINI...I16MAXI)) => Ok(I16(i as i16)), - (&ty::TyInt(IntTy::I32), InferSigned(i @ I32MINI...I32MAXI)) => Ok(I32(i as i32)), + (&ty::TyInt(IntTy::I8), InferSigned(i)) => Ok(I8(i as i8)), + (&ty::TyInt(IntTy::I16), InferSigned(i)) => Ok(I16(i as i16)), + (&ty::TyInt(IntTy::I32), InferSigned(i)) => Ok(I32(i as i32)), (&ty::TyInt(IntTy::I64), InferSigned(i)) => Ok(I64(i)), (&ty::TyInt(IntTy::Is), InferSigned(i)) => { match ConstIsize::new(i, tcx.sess.target.int_type) { Ok(val) => Ok(Isize(val)), - Err(e) => Err(err(e.into())), + Err(_) => Ok(Isize(ConstIsize::Is32(i as i32))), } }, - (&ty::TyInt(_), InferSigned(_)) => Err(err(Math(ConstMathErr::NotInRange))), - (&ty::TyUint(UintTy::U8), Infer(i @ 0...U8MAX)) => Ok(U8(i as u8)), - (&ty::TyUint(UintTy::U16), Infer(i @ 0...U16MAX)) => Ok(U16(i as u16)), - (&ty::TyUint(UintTy::U32), Infer(i @ 0...U32MAX)) => Ok(U32(i as u32)), + (&ty::TyUint(UintTy::U8), Infer(i)) => Ok(U8(i as u8)), + (&ty::TyUint(UintTy::U16), Infer(i)) => Ok(U16(i as u16)), + (&ty::TyUint(UintTy::U32), Infer(i)) => Ok(U32(i as u32)), (&ty::TyUint(UintTy::U64), Infer(i)) => Ok(U64(i)), (&ty::TyUint(UintTy::Us), Infer(i)) => { match ConstUsize::new(i, tcx.sess.target.uint_type) { Ok(val) => Ok(Usize(val)), - Err(e) => Err(err(e.into())), + Err(_) => Ok(Usize(ConstUsize::Us32(i as u32))), } }, - (&ty::TyUint(_), Infer(_)) => Err(err(Math(ConstMathErr::NotInRange))), (&ty::TyUint(_), InferSigned(_)) => Err(err(IntermediateUnsignedNegative)), (&ty::TyInt(ity), i) => Err(err(TypeMismatch(ity.to_string(), i))), @@ -1115,19 +1093,25 @@ fn cast_const_int<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstInt, ty: ty::Ty) -> CastRe match ty.sty { ty::TyBool if v == 0 => Ok(Bool(false)), ty::TyBool if v == 1 => Ok(Bool(true)), - ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i8))), - ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i16))), - ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i32))), + ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i64 as i8))), + ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i64 as i16))), + ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i64 as i32))), ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i64))), ty::TyInt(ast::IntTy::Is) => { - Ok(Integral(Isize(try!(ConstIsize::new(v as i64, tcx.sess.target.int_type))))) + match ConstIsize::new(v as i64, tcx.sess.target.int_type) { + Ok(val) => Ok(Integral(Isize(val))), + Err(_) => Ok(Integral(Isize(ConstIsize::Is32(v as i64 as i32)))), + } }, ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))), ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))), ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))), - ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v as u64))), + ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v))), ty::TyUint(ast::UintTy::Us) => { - Ok(Integral(Usize(try!(ConstUsize::new(v, tcx.sess.target.uint_type))))) + match ConstUsize::new(v, tcx.sess.target.uint_type) { + Ok(val) => Ok(Integral(Usize(val))), + Err(_) => Ok(Integral(Usize(ConstUsize::Us32(v as u32)))), + } }, ty::TyFloat(ast::FloatTy::F64) if val.is_negative() => { // FIXME: this could probably be prettier @@ -1174,57 +1158,37 @@ fn lit_to_const<'tcx>(lit: &ast::LitKind, tcx: &TyCtxt<'tcx>, ty_hint: Option>, span: Span, - ) -> Result { + ) -> Result { use syntax::ast::*; use syntax::ast::LitIntType::*; - const I8MAX: u64 = ::std::i8::MAX as u64; - const I16MAX: u64 = ::std::i16::MAX as u64; - const I32MAX: u64 = ::std::i32::MAX as u64; - const I64MAX: u64 = ::std::i64::MAX as u64; - const U8MAX: u64 = ::std::u8::MAX as u64; - const U16MAX: u64 = ::std::u16::MAX as u64; - const U32MAX: u64 = ::std::u32::MAX as u64; - const U64MAX: u64 = ::std::u64::MAX as u64; match *lit { LitKind::Str(ref s, _) => Ok(Str((*s).clone())), LitKind::ByteStr(ref data) => Ok(ByteStr(data.clone())), LitKind::Byte(n) => Ok(Integral(U8(n))), - LitKind::Int(n @ 0...I8MAX, Signed(IntTy::I8)) => Ok(Integral(I8(n as i8))), - LitKind::Int(n @ 0...I16MAX, Signed(IntTy::I16)) => Ok(Integral(I16(n as i16))), - LitKind::Int(n @ 0...I32MAX, Signed(IntTy::I32)) => Ok(Integral(I32(n as i32))), - LitKind::Int(n @ 0...I64MAX, Signed(IntTy::I64)) => Ok(Integral(I64(n as i64))), - LitKind::Int(n, Signed(IntTy::Is)) => { - Ok(Integral(Isize(try!(ConstIsize::new(n as i64, tcx.sess.target.int_type))))) + LitKind::Int(n, Signed(ity)) => { + infer(InferSigned(n as i64), tcx, &ty::TyInt(ity), span).map(Integral) }, - LitKind::Int(_, Signed(ty)) => Err(ConstMathErr::LitOutOfRange(ty)), - LitKind::Int(n, Unsuffixed) => { match ty_hint.map(|t| &t.sty) { Some(&ty::TyInt(ity)) => { - lit_to_const(&LitKind::Int(n, Signed(ity)), tcx, ty_hint, span) + infer(InferSigned(n as i64), tcx, &ty::TyInt(ity), span).map(Integral) }, Some(&ty::TyUint(uty)) => { - lit_to_const(&LitKind::Int(n, Unsigned(uty)), tcx, ty_hint, span) + infer(Infer(n), tcx, &ty::TyUint(uty), span).map(Integral) }, None => Ok(Integral(Infer(n))), Some(&ty::TyEnum(ref adt, _)) => { let hints = tcx.lookup_repr_hints(adt.did); let int_ty = tcx.enum_repr_type(hints.iter().next()); - lit_to_const(lit, tcx, Some(int_ty.to_ty(tcx)), span) + infer(Infer(n), tcx, &int_ty.to_ty(tcx).sty, span).map(Integral) }, Some(ty_hint) => panic!("bad ty_hint: {:?}, {:?}", ty_hint, lit), } }, - LitKind::Int(n @ 0...U8MAX, Unsigned(UintTy::U8)) => Ok(Integral(U8(n as u8))), - LitKind::Int(n @ 0...U16MAX, Unsigned(UintTy::U16)) => Ok(Integral(U16(n as u16))), - LitKind::Int(n @ 0...U32MAX, Unsigned(UintTy::U32)) => Ok(Integral(U32(n as u32))), - LitKind::Int(n @ 0...U64MAX, Unsigned(UintTy::U64)) => Ok(Integral(U64(n as u64))), - - LitKind::Int(n, Unsigned(UintTy::Us)) => { - Ok(Integral(Usize(try!(ConstUsize::new(n as u64, tcx.sess.target.uint_type))))) + LitKind::Int(n, Unsigned(ity)) => { + infer(Infer(n), tcx, &ty::TyUint(ity), span).map(Integral) }, - LitKind::Int(_, Unsigned(ty)) => Err(ConstMathErr::ULitOutOfRange(ty)), LitKind::Float(ref n, _) | LitKind::FloatUnsuffixed(ref n) => { diff --git a/src/librustc_const_eval/int.rs b/src/librustc_const_eval/int.rs index bb72711379eaa..15253987bf2b2 100644 --- a/src/librustc_const_eval/int.rs +++ b/src/librustc_const_eval/int.rs @@ -73,11 +73,11 @@ impl ConstInt { (_, InferSigned(_)) | (_, Infer(_)) => return other.infer(self).map(|(b, a)| (a, b)), - (Infer(a @ 0...as_u64::I8MAX), I8(_)) => I8(a as i8), - (Infer(a @ 0...as_u64::I16MAX), I16(_)) => I16(a as i16), - (Infer(a @ 0...as_u64::I32MAX), I32(_)) => I32(a as i32), + (Infer(a @ 0...as_u64::I8MAX), I8(_)) => I8(a as i64 as i8), + (Infer(a @ 0...as_u64::I16MAX), I16(_)) => I16(a as i64 as i16), + (Infer(a @ 0...as_u64::I32MAX), I32(_)) => I32(a as i64 as i32), (Infer(a @ 0...as_u64::I64MAX), I64(_)) => I64(a as i64), - (Infer(a @ 0...as_u64::I32MAX), Isize(Is32(_))) => Isize(Is32(a as i32)), + (Infer(a @ 0...as_u64::I32MAX), Isize(Is32(_))) => Isize(Is32(a as i64 as i32)), (Infer(a @ 0...as_u64::I64MAX), Isize(Is64(_))) => Isize(Is64(a as i64)), (Infer(a @ 0...as_u64::U8MAX), U8(_)) => U8(a as u8), (Infer(a @ 0...as_u64::U16MAX), U16(_)) => U16(a as u16), diff --git a/src/test/compile-fail/enum-discrim-too-small2.rs b/src/test/compile-fail/enum-discrim-too-small2.rs index a25d0a9e1871f..d66716c14c3d5 100644 --- a/src/test/compile-fail/enum-discrim-too-small2.rs +++ b/src/test/compile-fail/enum-discrim-too-small2.rs @@ -8,32 +8,35 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![deny(overflowing_literals)] +#![allow(dead_code)] + #[repr(i8)] enum Ei8 { Ai8 = 23, Bi8 = -23, - Ci8 = 223, //~ ERROR literal out of range for i8 [E0080] + Ci8 = 223, //~ ERROR literal out of range for i8 } #[repr(i16)] enum Ei16 { Ai16 = 23, Bi16 = -22333, - Ci16 = 55555, //~ ERROR literal out of range for i16 [E0080] + Ci16 = 55555, //~ ERROR literal out of range for i16 } #[repr(i32)] enum Ei32 { Ai32 = 23, Bi32 = -2_000_000_000, - Ci32 = 3_000_000_000, //~ ERROR literal out of range for i32 [E0080] + Ci32 = 3_000_000_000, //~ ERROR literal out of range for i32 } #[repr(i64)] enum Ei64 { Ai64 = 23, Bi64 = -9223372036854775808, - Ci64 = 9223372036854775809, //~ ERROR literal out of range for i64 [E0080] + Ci64 = 9223372036854775809, //~ ERROR literal out of range for i64 } // u64 currently allows negative numbers, and i64 allows numbers greater than `1<<63`. This is a diff --git a/src/test/compile-fail/lint-type-limits2.rs b/src/test/compile-fail/lint-type-limits2.rs index 20e756c803a9c..8fc18d16469e3 100644 --- a/src/test/compile-fail/lint-type-limits2.rs +++ b/src/test/compile-fail/lint-type-limits2.rs @@ -19,5 +19,6 @@ fn bar() -> i8 { } fn baz() -> bool { - 128 > bar() //~ ERROR literal out of range for i8 + 128 > bar() //~ ERROR comparison is useless due to type limits + //~| WARN literal out of range for i8 } diff --git a/src/test/compile-fail/lint-type-limits3.rs b/src/test/compile-fail/lint-type-limits3.rs index fed4143a6d7a2..b09dc0acdbf91 100644 --- a/src/test/compile-fail/lint-type-limits3.rs +++ b/src/test/compile-fail/lint-type-limits3.rs @@ -15,7 +15,8 @@ fn main() { } fn qux() { let mut i = 1i8; - while 200 != i { //~ ERROR literal out of range for i8 + while 200 != i { //~ ERROR comparison is useless due to type limits + //~| WARN literal out of range for i8 i += 1; } } diff --git a/src/test/run-pass/const-negation.rs b/src/test/run-pass/const-negation.rs index afd82e7d8aced..96f4217e4cb80 100644 --- a/src/test/run-pass/const-negation.rs +++ b/src/test/run-pass/const-negation.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(stmt_expr_attributes)] + #[deny(const_err)] fn main() { @@ -18,7 +20,10 @@ fn main() { assert_eq!(::std::i32::MIN as u64, 0xffffffff80000000); assert_eq!(-2147483648isize as u64, 0xffffffff80000000); assert_eq!(::std::i64::MIN as u64, 0x8000000000000000); + #[cfg(target_pointer_width = "64")] assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000); + #[cfg(target_pointer_width = "32")] + assert_eq!(-9223372036854775808isize as u64, 0); const J: usize = ::std::i32::MAX as usize; const K: usize = -1i32 as u32 as usize; const L: usize = ::std::i32::MIN as usize; From fcee002edfd1867ab49d9625de6c7907f484a7a9 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 10 Mar 2016 14:20:53 +0100 Subject: [PATCH 11/13] break enum variant discriminant inference in case of 32-bit `assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000);` fails on 32 bit and succeeds on 64 bit. These commits don't change that behavior. The following worked before my changes, because the discriminant was always processed as `u64`. Now it fails, because the discriminant of `Bu64` is now `0` instead of `-9223372036854775808`. This is more in line with the above assertion's code, since `-9223372036854775808isize as u64` on 32 bit yielded `0`. ```rust enum Eu64 { Au64 = 0, Bu64 = 0x8000_0000_0000_0000 } ``` --- src/test/run-pass/enum-discrim-autosizing.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/run-pass/enum-discrim-autosizing.rs b/src/test/run-pass/enum-discrim-autosizing.rs index 99e44735d0f03..53c44f2bb24b5 100644 --- a/src/test/run-pass/enum-discrim-autosizing.rs +++ b/src/test/run-pass/enum-discrim-autosizing.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(stmt_expr_attributes)] use std::mem::size_of; @@ -46,11 +47,6 @@ enum Ei64 { Bi64 = 0x8000_0000 } -enum Eu64 { - Au64 = 0, - Bu64 = 0x8000_0000_0000_0000 -} - pub fn main() { assert_eq!(size_of::(), 1); assert_eq!(size_of::(), 1); @@ -58,6 +54,8 @@ pub fn main() { assert_eq!(size_of::(), 2); assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 4); + #[cfg(target_pointer_width = "64")] assert_eq!(size_of::(), 8); - assert_eq!(size_of::(), 8); + #[cfg(target_pointer_width = "32")] + assert_eq!(size_of::(), 4); } From 08c448a7d3cf4ed06cf731a3458a64b97b2a6d9b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 11 Mar 2016 09:44:11 +0100 Subject: [PATCH 12/13] undo changes to run-pass tests that aren't errors anymore --- src/test/run-pass/packed-struct-generic-layout.rs | 4 ++-- src/test/run-pass/shift.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/run-pass/packed-struct-generic-layout.rs b/src/test/run-pass/packed-struct-generic-layout.rs index 1c8a1322ad2a9..b962a5c793382 100644 --- a/src/test/run-pass/packed-struct-generic-layout.rs +++ b/src/test/run-pass/packed-struct-generic-layout.rs @@ -20,7 +20,7 @@ struct S { pub fn main() { unsafe { - let s = S { a: 0xff_ff_ff_ffu32, b: 1, c: 0xaa_aa_aa_aa as u32 }; + let s = S { a: 0xff_ff_ff_ffu32, b: 1, c: 0xaa_aa_aa_aa as i32 }; let transd : [u8; 9] = mem::transmute(s); // Don't worry about endianness, the numbers are palindromic. assert_eq!(transd, @@ -29,7 +29,7 @@ pub fn main() { 0xaa, 0xaa, 0xaa, 0xaa]); - let s = S { a: 1u8, b: 2u8, c: 0b10000001_10000001 as u16}; + let s = S { a: 1u8, b: 2u8, c: 0b10000001_10000001 as i16}; let transd : [u8; 4] = mem::transmute(s); // Again, no endianness problems. assert_eq!(transd, diff --git a/src/test/run-pass/shift.rs b/src/test/run-pass/shift.rs index 5ca6a02309927..2f417e0e2f4e8 100644 --- a/src/test/run-pass/shift.rs +++ b/src/test/run-pass/shift.rs @@ -45,7 +45,7 @@ fn test_expr() { let v4 = 4 as isize; let v2 = 2 as isize; assert_eq!(v10 >> v2 as usize, v2 as i8); - assert_eq!(v10 << v2 as usize, 40 as i8); + assert_eq!(v10 << v4 as usize, 160 as i8); let v10 = 10 as usize; let v4 = 4 as isize; @@ -71,9 +71,9 @@ fn test_const() { assert_eq!(r2_3, 160 as isize); static r1_4: i8 = 10i8 >> 2_usize; - static r2_4: i8 = 10i8 << 2_usize; + static r2_4: i8 = 10i8 << 4_usize; assert_eq!(r1_4, 2 as i8); - assert_eq!(r2_4, 40 as i8); + assert_eq!(r2_4, 160 as i8); static r1_5: usize = 10_usize >> 2_usize; static r2_5: usize = 10_usize << 4_usize; From f665c399a013ce27991fafb2203a5420e75f9cfc Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 14 Mar 2016 09:29:18 +0100 Subject: [PATCH 13/13] rustbuild --- src/librustc/Cargo.toml | 1 + src/librustc_const_eval/Cargo.toml | 14 ++++++++++++++ src/librustc_metadata/Cargo.toml | 1 + src/librustc_mir/Cargo.toml | 1 + src/librustc_trans/Cargo.toml | 1 + src/librustc_typeck/Cargo.toml | 1 + 6 files changed, 19 insertions(+) create mode 100644 src/librustc_const_eval/Cargo.toml diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index e65b4355e9d37..1f000c2af68b8 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -18,6 +18,7 @@ log = { path = "../liblog" } rbml = { path = "../librbml" } rustc_back = { path = "../librustc_back" } rustc_bitflags = { path = "../librustc_bitflags" } +rustc_const_eval = { path = "../librustc_const_eval" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_front = { path = "../librustc_front" } rustc_llvm = { path = "../librustc_llvm" } diff --git a/src/librustc_const_eval/Cargo.toml b/src/librustc_const_eval/Cargo.toml new file mode 100644 index 0000000000000..f885e9a94d522 --- /dev/null +++ b/src/librustc_const_eval/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_const_eval" +version = "0.0.0" + +[lib] +name = "rustc_const_eval" +path = "lib.rs" +crate-type = ["dylib"] + +[dependencies] +log = { path = "../liblog" } +serialize = { path = "../libserialize" } +syntax = { path = "../libsyntax" } diff --git a/src/librustc_metadata/Cargo.toml b/src/librustc_metadata/Cargo.toml index d6bb4b157a6a3..e8b5a7efdd96b 100644 --- a/src/librustc_metadata/Cargo.toml +++ b/src/librustc_metadata/Cargo.toml @@ -15,6 +15,7 @@ rbml = { path = "../librbml" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } rustc_bitflags = { path = "../librustc_bitflags" } +rustc_const_eval = { path = "../librustc_const_eval" } rustc_front = { path = "../librustc_front" } rustc_llvm = { path = "../librustc_llvm" } serialize = { path = "../libserialize" } diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 93817ab0db61b..99237c9fa5f47 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -13,6 +13,7 @@ graphviz = { path = "../libgraphviz" } log = { path = "../liblog" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } +rustc_const_eval = { path = "../librustc_const_eval" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_front = { path = "../librustc_front" } syntax = { path = "../libsyntax" } diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index 24320c4d563bc..b7faafeba9aa1 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -16,6 +16,7 @@ graphviz = { path = "../libgraphviz" } log = { path = "../liblog" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } +rustc_const_eval = { path = "../librustc_const_eval" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_front = { path = "../librustc_front" } rustc_llvm = { path = "../librustc_llvm" } diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index fd33e9da1f937..1c907972863b6 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -15,5 +15,6 @@ arena = { path = "../libarena" } fmt_macros = { path = "../libfmt_macros" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } +rustc_const_eval = { path = "../librustc_const_eval" } rustc_front = { path = "../librustc_front" } rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }