diff --git a/mk/crates.mk b/mk/crates.mk index 4c06afcae0c42..0df4384784e12 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -106,7 +106,7 @@ DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_bo rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \ rustc_trans rustc_privacy rustc_lint rustc_plugin \ rustc_metadata syntax_ext rustc_passes rustc_save_analysis rustc_const_eval -DEPS_rustc_lint := rustc log syntax rustc_const_eval +DEPS_rustc_lint := rustc log syntax rustc_const_eval rustc_const_math DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags DEPS_rustc_metadata := rustc syntax rbml rustc_const_math DEPS_rustc_passes := syntax rustc core rustc_const_eval diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 738a04dea585d..a6dc6052cd7d1 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -76,6 +76,8 @@ use syntax::parse::token; use syntax::std_inject; use syntax::visit::{self, Visitor}; +use rustc_const_math::{ConstVal, ConstInt, ConstIsize, ConstUsize}; + use std::cell::{Cell, RefCell}; pub struct LoweringContext<'a> { @@ -1142,12 +1144,24 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { let rhs = lower_expr(lctx, rhs); hir::ExprBinary(binop, lhs, rhs) } + ExprKind::Unary(UnOp::Neg, ref inner) => { + let (new, mut attrs) = lower_un_neg(lctx, inner); + attrs.update(|a| { + a.prepend(e.attrs.clone()) + }); + return P(hir::Expr { + id: e.id, + span: e.span, + attrs: attrs, + node: new, + }); + }, ExprKind::Unary(op, ref ohs) => { let op = lower_unop(lctx, op); let ohs = lower_expr(lctx, ohs); hir::ExprUnary(op, ohs) } - ExprKind::Lit(ref l) => hir::ExprLit(P((**l).clone())), + ExprKind::Lit(ref l) => hir::ExprLit(lower_lit(lctx, &l.node, l.span)), ExprKind::Cast(ref expr, ref ty) => { let expr = lower_expr(lctx, expr); hir::ExprCast(expr, lower_ty(lctx, ty)) @@ -1674,6 +1688,121 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { }) } +fn negate_lit(lctx: &LoweringContext, n: u64, ty: LitIntType) -> ConstVal { + use syntax::ast::LitIntType::*; + use syntax::ast::IntTy::*; + use std::{i8, i16, i32, i64}; + use rustc_const_math::ConstVal::Integral; + const I8_OVERFLOW: u64 = i8::MAX as u64 + 1; + const I16_OVERFLOW: u64 = i16::MAX as u64 + 1; + const I32_OVERFLOW: u64 = i32::MAX as u64 + 1; + const I64_OVERFLOW: u64 = i64::MAX as u64 + 1; + match (n, ty, lctx.id_assigner.target_bitwidth()) { + (I8_OVERFLOW, Signed(I8), _) => Integral(ConstInt::I8(i8::MIN)), + (I16_OVERFLOW, Signed(I16), _) => Integral(ConstInt::I16(i16::MIN)), + (I32_OVERFLOW, Signed(I32), _) => Integral(ConstInt::I32(i32::MIN)), + (I64_OVERFLOW, Signed(I64), _) => Integral(ConstInt::I64(i64::MIN)), + (I64_OVERFLOW, Signed(Is), 64) => Integral(ConstInt::Isize(ConstIsize::Is64(i64::MIN))), + (I32_OVERFLOW, Signed(Is), 32) => Integral(ConstInt::Isize(ConstIsize::Is32(i32::MIN))), + (I64_OVERFLOW, Unsuffixed, _) => Integral(ConstInt::InferSigned(i64::MIN)), + (n, Signed(I8), _) => Integral(ConstInt::I8(-(n as i64 as i8))), + (n, Signed(I16), _) => Integral(ConstInt::I16(-(n as i64 as i16))), + (n, Signed(I32), _) => Integral(ConstInt::I32(-(n as i64 as i32))), + (n, Signed(I64), _) => Integral(ConstInt::I64(-(n as i64))), + (n, Signed(Is), 64) => Integral(ConstInt::Isize(ConstIsize::Is64(-(n as i64)))), + (n, Signed(Is), 32) => Integral(ConstInt::Isize(ConstIsize::Is32(-(n as i64 as i32)))), + (_, Signed(Is), _) => unreachable!(), + // unary negation of unsigned has already been reported by EarlyTypeLimits + (_, Unsigned(_), _) => ConstVal::Dummy, + (n, Unsuffixed, _) => Integral(ConstInt::InferSigned(-(n as i64))), + } +} + +pub fn lower_un_neg(lctx: &LoweringContext, inner: &Expr) -> (hir::Expr_, ThinAttributes) { + match inner.node { + ExprKind::Paren(ref ex) => { + let (new, mut attrs) = lower_un_neg(lctx, ex); + attrs.update(|attrs| attrs.prepend(ex.attrs.clone())); + return (new, attrs); + } + ExprKind::Lit(ref lit) => { + if let LitKind::Int(n, ty) = lit.node { + return (hir::ExprLit(negate_lit(lctx, n, ty)), None); + } + }, + ExprKind::Unary(UnOp::Neg, ref double_inner) => { + if let ExprKind::Lit(ref lit) = double_inner.node { + // skip double negation where applicable + if let LitKind::Int(..) = lit.node { + return ( + hir::ExprLit(lower_lit(lctx, &lit.node, inner.span)), + double_inner.attrs.clone(), + ); + } + } + }, + _ => {}, + } + (hir::ExprUnary(hir::UnNeg, lower_expr(lctx, inner)), None) +} + +pub fn lower_lit(lctx: &LoweringContext, lit: &LitKind, span: Span) -> ConstVal { + use syntax::ast::LitIntType::*; + use syntax::ast::LitKind::*; + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + match *lit { + Str(ref s, _) => ConstVal::Str((*s).clone()), + ByteStr(ref data) => ConstVal::ByteStr(data.clone()), + Byte(n) => ConstVal::Integral(ConstInt::U8(n)), + Int(n, Signed(I8)) => ConstVal::Integral(ConstInt::I8(n as i64 as i8)), + Int(n, Signed(I16)) => ConstVal::Integral(ConstInt::I16(n as i64 as i16)), + Int(n, Signed(I32)) => ConstVal::Integral(ConstInt::I32(n as i64 as i32)), + Int(n, Signed(I64)) => ConstVal::Integral(ConstInt::I64(n as i64)), + Int(n, Signed(Is)) => { + ConstVal::Integral(match lctx.id_assigner.target_bitwidth() { + 32 => ConstInt::Isize(ConstIsize::Is32(n as i64 as i32)), + 64 => ConstInt::Isize(ConstIsize::Is64(n as i64)), + _ => unimplemented!(), + }) + }, + Int(n, Unsigned(U8)) => ConstVal::Integral(ConstInt::U8(n as u8)), + Int(n, Unsigned(U16)) => ConstVal::Integral(ConstInt::U16(n as u16)), + Int(n, Unsigned(U32)) => ConstVal::Integral(ConstInt::U32(n as u32)), + Int(n, Unsigned(U64)) => ConstVal::Integral(ConstInt::U64(n)), + Int(n, Unsigned(Us)) => { + ConstVal::Integral(match lctx.id_assigner.target_bitwidth() { + 32 => ConstInt::Usize(ConstUsize::Us32(n as u32)), + 64 => ConstInt::Usize(ConstUsize::Us64(n)), + _ => unimplemented!(), + }) + }, + Int(n, Unsuffixed) => ConstVal::Integral(ConstInt::Infer(n)), + Float(ref f, fty) => { + if let Ok(x) = f.parse::() { + ConstVal::Float(x, Some(fty)) + } else { + // FIXME(#31407) this is only necessary because float parsing is buggy + lctx.id_assigner + .diagnostic() + .span_bug(span, "could not evaluate float literal (see issue #31407)"); + } + }, + FloatUnsuffixed(ref f) => { + if let Ok(x) = f.parse::() { + ConstVal::Float(x, None) + } else { + // FIXME(#31407) this is only necessary because float parsing is buggy + lctx.id_assigner + .diagnostic() + .span_bug(span, "could not evaluate float literal (see issue #31407)"); + } + }, + Bool(b) => ConstVal::Bool(b), + Char(c) => ConstVal::Char(c), + } +} + pub fn lower_stmt(lctx: &LoweringContext, s: &Stmt) -> hir::Stmt { match s.node { StmtKind::Decl(ref d, id) => { diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index edb9b78352765..1fd7d27049bfd 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -40,12 +40,14 @@ use util::nodemap::{NodeMap, FnvHashSet}; use syntax::codemap::{self, Span, Spanned, DUMMY_SP, ExpnId}; use syntax::abi::Abi; use syntax::ast::{Name, NodeId, DUMMY_NODE_ID, TokenTree, AsmDialect}; -use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, MetaItem}; +use syntax::ast::{Attribute, StrStyle, FloatTy, IntTy, UintTy, MetaItem}; use syntax::attr::{ThinAttributes, ThinAttributesExt}; use syntax::parse::token::InternedString; use syntax::ptr::P; use std::collections::BTreeMap; +use rustc_const_math::ConstVal; + use std::fmt; use std::hash::{Hash, Hasher}; use serialize::{Encodable, Decodable, Encoder, Decoder}; @@ -930,7 +932,7 @@ pub enum Expr_ { /// A unary operation (For example: `!x`, `*x`) ExprUnary(UnOp, P), /// A literal (For example: `1`, `"foo"`) - ExprLit(P), + ExprLit(ConstVal), /// A cast (`foo as f64`) ExprCast(P, P), ExprType(P, P), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index cd2dfd4463952..5b9080c983076 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -23,10 +23,13 @@ use syntax::print::pp::Breaks::{Consistent, Inconsistent}; use syntax::print::pprust::{self as ast_pp, PrintState}; use syntax::ptr::P; +use rustc_const_math::ConstVal; + use hir; use hir::{Crate, PatKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; use std::io::{self, Write, Read}; +use std::ascii; pub enum AnnNode<'a> { NodeName(&'a ast::Name), @@ -1303,6 +1306,29 @@ impl<'a> State<'a> { self.print_expr_maybe_paren(expr) } + fn print_const_val(&mut self, const_val: &ConstVal) -> io::Result<()> { + match *const_val { + ConstVal::Float(f, None) => word(&mut self.s, &format!("{}", f)), + ConstVal::Float(f, Some(t)) => { + word(&mut self.s, &format!("{}_{}", f, t.ty_to_string())) + }, + ConstVal::Integral(ref i) => word(&mut self.s, &format!("{}", i)), + ConstVal::Str(ref s) => word(&mut self.s, &format!("{:?}", s)), + ConstVal::ByteStr(ref v) => { + let mut escaped: String = String::new(); + for &ch in v.iter() { + escaped.extend(ascii::escape_default(ch) + .map(|c| c as char)); + } + word(&mut self.s, &format!("b\"{}\"", escaped)) + }, + ConstVal::Bool(true) => word(&mut self.s, "true"), + ConstVal::Bool(false) => word(&mut self.s, "false"), + ConstVal::Char(c) => word(&mut self.s, &format!("{:?}", c)), + _ => unimplemented!(), + } + } + pub fn print_expr(&mut self, expr: &hir::Expr) -> io::Result<()> { self.maybe_print_comment(expr.span.lo)?; self.ibox(indent_unit)?; @@ -1339,8 +1365,8 @@ impl<'a> State<'a> { hir::ExprAddrOf(m, ref expr) => { self.print_expr_addr_of(m, &expr)?; } - hir::ExprLit(ref lit) => { - self.print_literal(&lit)?; + hir::ExprLit(ref const_val) => { + self.print_const_val(&const_val)?; } hir::ExprCast(ref expr, ref ty) => { self.print_expr(&expr)?; diff --git a/src/librustc/hir/svh.rs b/src/librustc/hir/svh.rs index 7ae20f68ad0c2..eb6ea80cdea3c 100644 --- a/src/librustc/hir/svh.rs +++ b/src/librustc/hir/svh.rs @@ -138,6 +138,7 @@ mod svh_visitor { use hir::intravisit::{Visitor, FnKind}; use hir::*; use hir; + use rustc_const_math::ConstVal; use std::hash::{Hash, SipHasher}; @@ -232,7 +233,7 @@ mod svh_visitor { SawExprTup, SawExprBinary(hir::BinOp_), SawExprUnary(hir::UnOp), - SawExprLit(ast::LitKind), + SawExprLit(ConstVal), SawExprCast, SawExprType, SawExprIf, @@ -260,7 +261,7 @@ mod svh_visitor { ExprTup(..) => SawExprTup, ExprBinary(op, _, _) => SawExprBinary(op.node), ExprUnary(op, _) => SawExprUnary(op), - ExprLit(ref lit) => SawExprLit(lit.node.clone()), + ExprLit(ref lit) => SawExprLit(lit.clone()), ExprCast(..) => SawExprCast, ExprType(..) => SawExprType, ExprIf(..) => SawExprIf, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index deb2062777220..115e1e178ddd4 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -81,7 +81,6 @@ pub mod lint; pub mod middle { pub mod astconv_util; pub mod expr_use_visitor; // STAGE0: increase glitch immunity - pub mod const_val; pub mod const_qualif; pub mod cstore; pub mod dataflow; diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 28437fa13368a..5f6ea3aa60ea9 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -9,9 +9,8 @@ // except according to those terms. use graphviz::IntoCow; -use middle::const_val::ConstVal; -use rustc_const_math::{ConstUsize, ConstInt}; -use hir::def_id::DefId; +use rustc_const_math::{ConstUsize, ConstInt, ConstVal}; +use hir::def_id::{DefId, DefIndex}; use ty::subst::Substs; use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty}; use util::ppaux; @@ -999,9 +998,11 @@ impl<'tcx> Debug for Literal<'tcx> { /// Write a `ConstVal` in a way closer to the original source code than the `Debug` output. fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { - use middle::const_val::ConstVal::*; + use rustc_const_math::ConstVal::*; match *const_val { - Float(f) => write!(fmt, "{:?}", f), + Float(f, Some(ast::FloatTy::F32)) => write!(fmt, "{}_f32", f as f32), + Float(f, Some(ast::FloatTy::F64)) => write!(fmt, "{}_f64", f), + Float(f, None) => write!(fmt, "{}", f), Integral(n) => write!(fmt, "{}", n), Str(ref s) => write!(fmt, "{:?}", s), ByteStr(ref bytes) => { @@ -1012,7 +1013,10 @@ fn fmt_const_val(fmt: &mut W, const_val: &ConstVal) -> fmt::Result { write!(fmt, "b\"{}\"", escaped) } Bool(b) => write!(fmt, "{:?}", b), - Function(def_id) => write!(fmt, "{}", item_path_str(def_id)), + Function { krate, index } => { + let path = item_path_str(DefId { krate: krate, index: DefIndex::from_u32(index) }); + write!(fmt, "{}", path) + }, Struct(node_id) | Tuple(node_id) | Array(node_id, _) | Repeat(node_id, _) => write!(fmt, "{}", node_to_string(node_id)), Char(c) => write!(fmt, "{:?}", c), diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index ae803f502318e..b834fea499753 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -8,12 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use middle::const_val::ConstVal; use hir::def_id::DefId; use ty::subst::Substs; use ty::{ClosureSubsts, FnOutput, Region, Ty}; use mir::repr::*; -use rustc_const_math::ConstUsize; +use rustc_const_math::{ConstUsize, ConstVal}; use rustc_data_structures::tuple_slice::TupleSlice; use syntax::codemap::Span; diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index d3005ff2ded25..8f5dd7dd91c99 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -337,6 +337,16 @@ impl NodeIdAssigner for Session { fn diagnostic(&self) -> &errors::Handler { self.diagnostic() } + + fn target_bitwidth(&self) -> u32 { + match self.target.int_type { + ast::IntTy::Is => bug!("target int type defined as isize"), + ast::IntTy::I64 => 64, + ast::IntTy::I32 => 32, + ast::IntTy::I16 => unimplemented!(), + ast::IntTy::I8 => unimplemented!(), + } + } } fn split_msg_into_multilines(msg: &str) -> Option { diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 1e662d456d141..a0f3e9155bf14 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -13,7 +13,7 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc::dep_graph::DepNode; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use ::{eval_const_expr, eval_const_expr_partial, compare_const_vals}; use ::{const_expr_to_pat, lookup_const_by_id}; use ::EvalHint::ExprTypeChecked; @@ -37,7 +37,7 @@ use rustc::hir::{Pat, PatKind}; use rustc::hir::intravisit::{self, IdVisitor, IdVisitingOperation, Visitor, FnKind}; use rustc_back::slice; -use syntax::ast::{self, DUMMY_NODE_ID, NodeId}; +use syntax::ast::{DUMMY_NODE_ID, NodeId}; use syntax::codemap::{Span, Spanned, DUMMY_SP}; use rustc::hir::fold::{Folder, noop_fold_pat}; use rustc::hir::print::pat_to_string; @@ -275,7 +275,7 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) { pat.walk(|p| { if let PatKind::Lit(ref expr) = p.node { match eval_const_expr_partial(cx.tcx, &expr, ExprTypeChecked, None) { - Ok(ConstVal::Float(f)) if f.is_nan() => { + Ok(ConstVal::Float(f, _)) if f.is_nan() => { span_warn!(cx.tcx.sess, p.span, E0003, "unmatchable NaN in pattern, \ use the is_nan method in a guard instead"); @@ -423,13 +423,9 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir: } fn const_val_to_expr(value: &ConstVal) -> P { - let node = match value { - &ConstVal::Bool(b) => ast::LitKind::Bool(b), - _ => bug!() - }; P(hir::Expr { id: 0, - node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })), + node: hir::ExprLit(value.clone()), span: DUMMY_SP, attrs: None, }) diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 45a90bf00681c..143c8a7d85973 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -10,8 +10,7 @@ //#![allow(non_camel_case_types)] -use rustc::middle::const_val::ConstVal::*; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal::*; use self::ErrKind::*; use self::EvalHint::*; @@ -20,7 +19,7 @@ use rustc::hir::map::blocks::FnLikeNode; use rustc::middle::cstore::{self, CrateStore, InlinedItem}; use rustc::{infer, traits}; use rustc::hir::def::Def; -use rustc::hir::def_id::DefId; +use rustc::hir::def_id::{DefId, DefIndex}; use rustc::hir::pat_util::def_to_path; use rustc::ty::{self, Ty, TyCtxt, subst}; use rustc::ty::util::IntTypeExt; @@ -410,9 +409,9 @@ pub enum ErrKind { IndexOpFeatureGated, Math(ConstMathErr), - IntermediateUnsignedNegative, /// Expected, Got TypeMismatch(String, ConstInt), + FloatTypeMismatch, BadType(ConstVal), } @@ -471,15 +470,13 @@ impl ConstEvalErr { 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(), - 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(), + FloatTypeMismatch => "tried to apply a binary operation to values of \ + different float types".into_cow(), } } } @@ -562,30 +559,24 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, // 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)), _) => { + match (lit, ety.map(|t| &t.sty)) { + (&Integral(Infer(I8_OVERFLOW)), Some(&ty::TyInt(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)), _) => { + (&Integral(Infer(I16_OVERFLOW)), Some(&ty::TyInt(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)), _) => { + (&Integral(Infer(I32_OVERFLOW)), Some(&ty::TyInt(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)), _) => { + (&Integral(Infer(I64_OVERFLOW)), Some(&ty::TyInt(IntTy::I64))) => { return Ok(Integral(I64(::std::i64::MIN))) }, - (&LitKind::Int(n, Unsuffixed), Some(&ty::TyInt(IntTy::Is))) | - (&LitKind::Int(n, Signed(IntTy::Is)), _) => { + (&Integral(Infer(n)), Some(&ty::TyInt(IntTy::Is))) => { match tcx.sess.target.int_type { IntTy::I32 => if n == I32_OVERFLOW { return Ok(Integral(Isize(Is32(::std::i32::MIN)))); @@ -600,7 +591,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, } } match eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)? { - Float(f) => Float(-f), + Float(f, ft) => Float(-f, ft), Integral(i) => Integral(math!(e, -i)), const_val => signal!(e, NegateOn(const_val)), } @@ -624,13 +615,15 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, // not inferred match (eval_const_expr_partial(tcx, &a, ty_hint, fn_args)?, eval_const_expr_partial(tcx, &b, b_ty, fn_args)?) { - (Float(a), Float(b)) => { + (Float(_, Some(at)), Float(_, Some(bt))) if at != bt => signal!(e, FloatTypeMismatch), + (Float(a, at), Float(b, bt)) => { + let ft = at.or(bt); match op.node { - hir::BiAdd => Float(a + b), - hir::BiSub => Float(a - b), - hir::BiMul => Float(a * b), - hir::BiDiv => Float(a / b), - hir::BiRem => Float(a % b), + hir::BiAdd => Float(a + b, ft), + hir::BiSub => Float(a - b, ft), + hir::BiMul => Float(a * b, ft), + hir::BiDiv => Float(a / b, ft), + hir::BiRem => Float(a % b, ft), hir::BiEq => Bool(a == b), hir::BiLt => Bool(a < b), hir::BiLe => Bool(a <= b), @@ -765,7 +758,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, signal!(e, NonConstPath); } }, - Def::Method(id) | Def::Fn(id) => Function(id), + Def::Method(id) | + Def::Fn(id) => Function { krate: id.krate, index: id.index.as_u32() }, _ => signal!(e, NonConstPath), } } @@ -773,7 +767,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, let sub_ty_hint = ty_hint.erase_hint(); let callee_val = eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)?; let did = match callee_val { - Function(did) => did, + Function { krate, index } => DefId { krate: krate, index: DefIndex::from_u32(index) }, Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")), callee => signal!(e, CallOn(callee)), }; @@ -801,7 +795,12 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>, debug!("const call({:?})", call_args); eval_const_expr_partial(tcx, &result, ty_hint, Some(&call_args))? }, - hir::ExprLit(ref lit) => lit_to_const(&lit.node, tcx, ety, lit.span)?, + hir::ExprLit(ConstVal::Integral(i)) => if let Some(ref ty) = ety { + ConstVal::Integral(infer(i, tcx, &ty.sty, e.span)?) + } else { + ConstVal::Integral(i) + }, + hir::ExprLit(ref cv) => cv.clone(), hir::ExprBlock(ref block) => { match block.expr { Some(ref expr) => eval_const_expr_partial(tcx, &expr, ty_hint, fn_args)?, @@ -970,7 +969,7 @@ fn infer<'tcx>( Err(_) => Ok(Usize(ConstUsize::Us32(i as u32))), } }, - (&ty::TyUint(_), InferSigned(_)) => Err(err(IntermediateUnsignedNegative)), + (&ty::TyUint(_), InferSigned(_)) => Err(err(Math(ConstMathErr::UnsignedNegation))), (&ty::TyInt(ity), i) => Err(err(TypeMismatch(ity.to_string(), i))), (&ty::TyUint(ity), i) => Err(err(TypeMismatch(ity.to_string(), i))), @@ -1066,22 +1065,24 @@ fn cast_const_int<'tcx>(tcx: &TyCtxt<'tcx>, val: ConstInt, ty: ty::Ty) -> CastRe Err(_) => Ok(Integral(Usize(ConstUsize::Us32(v as u32)))), } }, - ty::TyFloat(ast::FloatTy::F64) if val.is_negative() => { + ty::TyFloat(ft @ 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 = (-val).map_err(Math)?; let val = val.to_u64().unwrap() as f64; let val = -val; - Ok(Float(val)) + Ok(Float(val, Some(ft))) }, - ty::TyFloat(ast::FloatTy::F64) => Ok(Float(val.to_u64().unwrap() as f64)), - ty::TyFloat(ast::FloatTy::F32) if val.is_negative() => { + ty::TyFloat(ft @ ast::FloatTy::F64) => Ok(Float(val.to_u64().unwrap() as f64, Some(ft))), + ty::TyFloat(ft @ ast::FloatTy::F32) if val.is_negative() => { let val = (-val).map_err(Math)?; let val = val.to_u64().unwrap() as f32; let val = -val; - Ok(Float(val as f64)) + Ok(Float(val as f64, Some(ft))) + }, + ty::TyFloat(ft @ ast::FloatTy::F32) => { + Ok(Float(val.to_u64().unwrap() as f32 as f64, Some(ft))) }, - ty::TyFloat(ast::FloatTy::F32) => Ok(Float(val.to_u64().unwrap() as f32 as f64)), ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")), _ => Err(CannotCast), } @@ -1092,8 +1093,8 @@ fn cast_const_float<'tcx>(tcx: &TyCtxt<'tcx>, f: f64, ty: ty::Ty) -> CastResult 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)), + ty::TyFloat(ft @ ast::FloatTy::F64) => Ok(Float(f, Some(ft))), + ty::TyFloat(ft @ ast::FloatTy::F32) => Ok(Float(f as f32 as f64, Some(ft))), _ => Err(CannotCast), } } @@ -1102,67 +1103,20 @@ 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), - Float(f) => cast_const_float(tcx, f, ty), + Float(f, _) => cast_const_float(tcx, f, ty), Char(c) => cast_const_int(tcx, Infer(c as u64), ty), - Function(_) => Err(UnimplementedConstVal("casting fn pointers")), + Function { .. } => Err(UnimplementedConstVal("casting fn pointers")), _ => Err(CannotCast), } } -fn lit_to_const<'tcx>(lit: &ast::LitKind, - tcx: &TyCtxt<'tcx>, - ty_hint: Option>, - span: Span, - ) -> Result { - use syntax::ast::*; - use syntax::ast::LitIntType::*; - 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, Signed(ity)) => { - infer(InferSigned(n as i64), tcx, &ty::TyInt(ity), span).map(Integral) - }, - - LitKind::Int(n, Unsuffixed) => { - match ty_hint.map(|t| &t.sty) { - Some(&ty::TyInt(ity)) => { - infer(InferSigned(n as i64), tcx, &ty::TyInt(ity), span).map(Integral) - }, - Some(&ty::TyUint(uty)) => { - 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()); - infer(Infer(n), tcx, &int_ty.to_ty(tcx).sty, span).map(Integral) - }, - Some(ty_hint) => bug!("bad ty_hint: {:?}, {:?}", ty_hint, lit), - } - }, - LitKind::Int(n, Unsigned(ity)) => { - infer(Infer(n), tcx, &ty::TyUint(ity), span).map(Integral) - }, - - LitKind::Float(ref n, _) | - LitKind::FloatUnsuffixed(ref n) => { - if let Ok(x) = n.parse::() { - Ok(Float(x)) - } else { - // FIXME(#31407) this is only necessary because float parsing is buggy - span_bug!(span, "could not evaluate float literal (see issue #31407)"); - } - } - LitKind::Bool(b) => Ok(Bool(b)), - LitKind::Char(c) => Ok(Char(c)), - } -} - pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option { match (a, b) { (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(), - (&Float(a), &Float(b)) => { + (&Float(a, at), &Float(b, bt)) => { + if at != bt { + return None; + } // This is pretty bad but it is the existing behavior. Some(if a == b { Ordering::Equal diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 9f66aac6e3899..169a3f0621057 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -36,8 +36,10 @@ mod int; mod us; mod is; mod err; +mod val; pub use int::*; pub use us::*; pub use is::*; pub use err::ConstMathErr; +pub use val::*; diff --git a/src/librustc/middle/const_val.rs b/src/librustc_const_math/val.rs similarity index 83% rename from src/librustc/middle/const_val.rs rename to src/librustc_const_math/val.rs index 3621cb267d91f..9641b36692e96 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc_const_math/val.rs @@ -11,22 +11,24 @@ use syntax::parse::token::InternedString; use syntax::ast; use std::rc::Rc; -use hir::def_id::DefId; use std::hash; use std::mem::transmute; -use rustc_const_math::*; use self::ConstVal::*; +use ConstInt; #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum ConstVal { - Float(f64), + Float(f64, Option), Integral(ConstInt), Str(InternedString), ByteStr(Rc>), Bool(bool), Struct(ast::NodeId), Tuple(ast::NodeId), - Function(DefId), + Function { + krate: ast::CrateNum, + index: u32, + }, Array(ast::NodeId, u64), Repeat(ast::NodeId, u64), Char(char), @@ -39,14 +41,14 @@ pub enum ConstVal { impl hash::Hash for ConstVal { fn hash(&self, state: &mut H) { match *self { - Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state), + Float(a, _) => unsafe { transmute::<_,u64>(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), Struct(a) => a.hash(state), Tuple(a) => a.hash(state), - Function(a) => a.hash(state), + Function { krate, index } => { krate.hash(state); index.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), @@ -62,14 +64,16 @@ impl hash::Hash for ConstVal { impl PartialEq for ConstVal { fn eq(&self, other: &ConstVal) -> bool { match (self, other) { - (&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}, + (&Float(a, _), &Float(b, _)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(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, (&Struct(a), &Struct(b)) => a == b, (&Tuple(a), &Tuple(b)) => a == b, - (&Function(a), &Function(b)) => a == b, + (&Function { krate, index }, &Function{ krate: bk, index: bi }) => { + (krate == bk) && (index == bi) + }, (&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, @@ -84,14 +88,14 @@ impl Eq for ConstVal { } impl ConstVal { pub fn description(&self) -> &'static str { match *self { - Float(_) => "float", + Float(_, _) => "float", Integral(i) => i.description(), Str(_) => "string literal", ByteStr(_) => "byte string literal", Bool(_) => "boolean", Struct(_) => "struct", Tuple(_) => "tuple", - Function(_) => "function definition", + Function {..} => "function definition", Array(..) => "array", Repeat(..) => "repeat", Char(..) => "char", diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f661b2a38b655..beedb0a03a236 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -119,6 +119,15 @@ pub fn compile_input(sess: &Session, Ok(())); let expanded_crate = assign_node_ids(sess, expanded_crate); + + time(sess.time_passes(), "attribute checking", || { + hir::check_attr::check_crate(sess, &expanded_crate); + }); + + time(sess.time_passes(), + "early lint checks", + || lint::check_ast_crate(sess, &expanded_crate)); + // Lower ast -> hir. let lcx = LoweringContext::new(sess, Some(&expanded_crate)); let dep_graph = DepGraph::new(sess.opts.build_dep_graph); @@ -153,14 +162,6 @@ pub fn compile_input(sess: &Session, Ok(())); } - time(sess.time_passes(), "attribute checking", || { - hir::check_attr::check_crate(sess, &expanded_crate); - }); - - time(sess.time_passes(), - "early lint checks", - || lint::check_ast_crate(sess, &expanded_crate)); - let opt_crate = if sess.opts.debugging_opts.keep_ast || sess.opts.debugging_opts.save_analysis { Some(&expanded_crate) diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 3f0cd397e769c..d126c71076727 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -13,4 +13,5 @@ log = { path = "../liblog" } rustc = { path = "../librustc" } rustc_back = { path = "../librustc_back" } rustc_const_eval = { path = "../librustc_const_eval" } +rustc_const_math = { path = "../librustc_const_math" } syntax = { path = "../libsyntax" } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 5e3a47701ebbf..d0591401d4217 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -38,6 +38,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::adjustment; use rustc::traits::{self, ProjectionMode}; use rustc::hir::map as hir_map; +use rustc_const_math::ConstVal; use util::nodemap::{NodeSet}; use lint::{Level, LateContext, LintContext, LintArray, Lint}; use lint::{LintPass, LateLintPass}; @@ -74,11 +75,9 @@ impl LintPass for WhileTrue { impl LateLintPass for WhileTrue { fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { if let hir::ExprWhile(ref cond, _, _) = e.node { - if let hir::ExprLit(ref lit) = cond.node { - if let ast::LitKind::Bool(true) = lit.node { - cx.span_lint(WHILE_TRUE, e.span, - "denote infinite loops with loop { ... }"); - } + if let hir::ExprLit(ConstVal::Bool(true)) = cond.node { + cx.span_lint(WHILE_TRUE, e.span, + "denote infinite loops with loop { ... }"); } } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 2075bd5edcaeb..4946d199f5b2e 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -46,6 +46,7 @@ extern crate rustc; extern crate log; extern crate rustc_back; extern crate rustc_const_eval; +extern crate rustc_const_math; pub use rustc::lint as lint; pub use rustc::middle as middle; @@ -79,7 +80,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { } macro_rules! add_early_builtin { - ($sess:ident, $($name:ident),*,) => ( + ($sess:ident, $($name:expr),*,) => ( {$( store.register_early_pass($sess, false, box $name); )*} @@ -102,6 +103,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { add_early_builtin!(sess, UnusedParens, + EarlyTypeLimits::new(), ); add_builtin!(sess, @@ -129,10 +131,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { PluginAsLibrary, DropWithReprExtern, MutableTransmutes, + TypeLimits, ); add_builtin_with_new!(sess, - TypeLimits, MissingDoc, MissingDebugImplementations, ); diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 891731cb29604..5a7c5ed058f96 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -12,14 +12,15 @@ use rustc::hir::def_id::DefId; use rustc::infer; +use rustc::session::Session; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; -use middle::const_val::ConstVal; +use rustc_const_math::{ConstVal, ConstInt}; use rustc_const_eval::eval_const_expr_partial; use rustc_const_eval::EvalHint::ExprTypeChecked; use util::nodemap::{FnvHashSet}; use lint::{LateContext, LintContext, LintArray}; -use lint::{LintPass, LateLintPass}; +use lint::{LintPass, LateLintPass, EarlyContext, EarlyLintPass}; use std::cmp; use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; @@ -70,6 +71,12 @@ declare_lint! { "literal out of range for its type" } +declare_lint! { + OVERFLOWING_SUFFIXED_LITERALS, + Warn, + "literal out of range for its type" +} + declare_lint! { EXCEEDING_BITSHIFTS, Deny, @@ -77,51 +84,124 @@ declare_lint! { } #[derive(Copy, Clone)] -pub struct TypeLimits { +pub struct TypeLimits; + +impl LintPass for TypeLimits { + fn get_lints(&self) -> LintArray { + lint_array!(UNUSED_COMPARISONS, OVERFLOWING_LITERALS, EXCEEDING_BITSHIFTS) + } +} + +#[derive(Copy, Clone)] +pub struct EarlyTypeLimits { /// Id of the last visited negated expression negated_expr_id: ast::NodeId, } -impl TypeLimits { - pub fn new() -> TypeLimits { - TypeLimits { +impl EarlyTypeLimits { + pub fn new() -> EarlyTypeLimits { + EarlyTypeLimits { negated_expr_id: !0, } } } -impl LintPass for TypeLimits { +impl LintPass for EarlyTypeLimits { fn get_lints(&self) -> LintArray { - lint_array!(UNUSED_COMPARISONS, OVERFLOWING_LITERALS, EXCEEDING_BITSHIFTS) + lint_array!(OVERFLOWING_SUFFIXED_LITERALS) } } -impl LateLintPass for TypeLimits { - fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { +impl EarlyLintPass for EarlyTypeLimits { + fn check_expr(&mut self, cx: &EarlyContext, e: &ast::Expr) { match e.node { - hir::ExprUnary(hir::UnNeg, ref expr) => { - if let hir::ExprLit(ref lit) = expr.node { - match lit.node { - ast::LitKind::Int(_, ast::LitIntType::Unsigned(_)) => { - forbid_unsigned_negation(cx, e.span); - }, - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { - if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty { - forbid_unsigned_negation(cx, e.span); - } - }, - _ => () - } - } else { - let t = cx.tcx.node_id_to_type(expr.id); - if let ty::TyUint(_) = t.sty { - forbid_unsigned_negation(cx, e.span); - } - } + ast::ExprKind::Unary(ast::UnOp::Neg, ref expr) => { // propagate negation, if the negation itself isn't negated if self.negated_expr_id != e.id { self.negated_expr_id = expr.id; } + } + ast::ExprKind::Paren(ref expr) => { + // forward negation + if self.negated_expr_id == e.id { + self.negated_expr_id = expr.id; + } + } + ast::ExprKind::Lit(ref lit) => { + use std::{i8, i16, i32, i64, u8, u16, u32, u64}; + use syntax::ast::LitKind::*; + use syntax::ast::LitIntType::*; + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + const I8_MAX: u64 = i8::MAX as u64; + const I16_MAX: u64 = i16::MAX as u64; + const I32_MAX: u64 = i32::MAX as u64; + const I64_MAX: u64 = i64::MAX as u64; + const U8_MAX: u64 = u8::MAX as u64; + const U16_MAX: u64 = u16::MAX as u64; + const U32_MAX: u64 = u32::MAX as u64; + const U64_MAX: u64 = u64::MAX as u64; + const I8_OVERFLOW: u64 = I8_MAX + 1; + const I16_OVERFLOW: u64 = I16_MAX + 1; + const I32_OVERFLOW: u64 = I32_MAX + 1; + const I64_OVERFLOW: u64 = I64_MAX + 1; + const I64_DENY: u64 = I64_OVERFLOW + 1; + match (&lit.node, cx.sess.target.int_type, cx.sess.target.uint_type) { + // The minimum value of a 2-complement type is -(max + 1) + (&Int(I8_OVERFLOW, Signed(I8)), _, _) | + (&Int(I16_OVERFLOW, Signed(I16)), _, _) | + (&Int(I32_OVERFLOW, Signed(I32)), _, _) | + (&Int(I64_OVERFLOW, Signed(I64)), _, _) | + (&Int(I64_OVERFLOW, Signed(Is)), I64, _) | + (&Int(I32_OVERFLOW, Signed(Is)), I32, _) if self.negated_expr_id == e.id => {}, + // Negating an unsigned suffixed literal + (&Int(_, Unsigned(_)), _, _) if self.negated_expr_id == e.id => { + forbid_unsigned_negation(cx.sess, e.span); + } + // unsuffixed values have a maximum when negating them + (&Int(I64_DENY...U64_MAX, Unsuffixed), _, _) if self.negated_expr_id == e.id + => { + cx.span_lint(OVERFLOWING_LITERALS, e.span, "literal out of range for i64"); + } + // ranges of the legal absolute value (-(max + 1) has been checked above) + (&Int(0...I8_MAX, Signed(I8)), _, _) | + (&Int(0...I16_MAX, Signed(I16)), _, _) | + (&Int(0...I32_MAX, Signed(I32)), _, _) | + (&Int(0...I64_MAX, Signed(I64)), _, _) | + (&Int(0...I32_MAX, Signed(Is)), I32, _) | + (&Int(0...I64_MAX, Signed(Is)), I64, _) | + (&Int(0...U8_MAX, Unsigned(U8)), _, _) | + (&Int(0...U16_MAX, Unsigned(U16)), _, _) | + (&Int(0...U32_MAX, Unsigned(U32)), _, _) | + (&Int(0...U64_MAX, Unsigned(U64)), _, _) | + (&Int(0...U32_MAX, Unsigned(Us)), _, U32) | + (&Int(0...U64_MAX, Unsigned(Us)), _, U64) | + // can't say anything about unsuffixed values, gets caught in a HIR-lint + (&Int(_, Unsuffixed), _, _) => {}, + // everything else is an overflow + (&Int(_, Signed(t)), _, _) => { + cx.span_lint(OVERFLOWING_LITERALS, e.span, + &format!("literal out of range for {:?}", t)); + } + (&Int(_, Unsigned(t)), _, _) => { + cx.span_lint(OVERFLOWING_LITERALS, e.span, + &format!("literal out of range for {:?}", t)); + } + _ => {}, + } + }, + _ => {}, + } + } +} + +impl LateLintPass for TypeLimits { + fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { + match e.node { + hir::ExprUnary(hir::UnNeg, ref expr) => { + if let ty::TyUint(_) = cx.tcx.node_id_to_type(expr.id).sty { + forbid_unsigned_negation(cx.sess(), e.span); + } }, hir::ExprBinary(binop, ref l, ref r) => { if is_comparison(binop) && !check_limits(cx.tcx, binop, &l, &r) { @@ -138,14 +218,13 @@ impl LateLintPass for TypeLimits { if let Some(bits) = opt_ty_bits { let exceeding = if let hir::ExprLit(ref lit) = r.node { - if let ast::LitKind::Int(shift, _) = lit.node { shift >= bits } - else { false } + if let ConstVal::Integral(i) = *lit { + i.is_negative() || i >= ConstInt::Infer(bits) + } else { false } } else { match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked, None) { Ok(ConstVal::Integral(i)) => { - i.is_negative() || i.to_u64() - .map(|i| i >= bits) - .unwrap_or(true) + i.is_negative() || i >= ConstInt::Infer(bits) }, _ => { false } } @@ -160,62 +239,65 @@ impl LateLintPass for TypeLimits { hir::ExprLit(ref lit) => { match cx.tcx.node_id_to_type(e.id).sty { ty::TyInt(t) => { - match lit.node { - ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | - ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => { + match *lit { + ConstVal::Integral(ConstInt::Infer(v)) => { let int_type = if let ast::IntTy::Is = t { cx.sess().target.int_type } else { t }; let (_, max) = int_ty_range(int_type); - let negative = self.negated_expr_id == e.id; - - // Detect literal value out of range [min, max] inclusive - // avoiding use of -min to prevent overflow/panic - if (negative && v > max as u64 + 1) || - (!negative && v > max as u64) { + if v > max as u64 { cx.span_lint(OVERFLOWING_LITERALS, e.span, &format!("literal out of range for {:?}", t)); return; } - } - _ => bug!() - }; + }, + ConstVal::Integral(ConstInt::InferSigned(v)) => { + let int_type = if let ast::IntTy::Is = t { + cx.sess().target.int_type + } else { + t + }; + let (min, max) = int_ty_range(int_type); + if v < min || v > max { + cx.span_lint(OVERFLOWING_LITERALS, e.span, + &format!("literal out of range for {:?}", t)); + return; + } + }, + // concrete types have already been checked in the lowering step + ConstVal::Integral(_) => return, + _ => bug!(), + } }, ty::TyUint(t) => { - let uint_type = if let ast::UintTy::Us = t { - cx.sess().target.uint_type - } else { - t - }; - let (min, max) = uint_ty_range(uint_type); - let lit_val: u64 = match lit.node { - // _v is u8, within range by definition - ast::LitKind::Byte(_v) => return, - ast::LitKind::Int(v, _) => v, - _ => bug!() - }; - if lit_val < min || lit_val > max { - cx.span_lint(OVERFLOWING_LITERALS, e.span, - &format!("literal out of range for {:?}", t)); + match *lit { + ConstVal::Integral(ConstInt::Infer(lit_val)) => { + let uint_type = if let ast::UintTy::Us = t { + cx.sess().target.uint_type + } else { + t + }; + let (min, max) = uint_ty_range(uint_type); + if lit_val < min || lit_val > max { + cx.span_lint(OVERFLOWING_LITERALS, e.span, + &format!("literal out of range for {:?}", t)); + } + }, + ConstVal::Integral(ConstInt::InferSigned(_)) => { + forbid_unsigned_negation(cx.sess(), e.span); + }, + _ => {}, } }, ty::TyFloat(t) => { let (min, max) = float_ty_range(t); - let lit_val: f64 = match lit.node { - ast::LitKind::Float(ref v, _) | - ast::LitKind::FloatUnsuffixed(ref v) => { - match v.parse() { - Ok(f) => f, - Err(_) => return - } + if let ConstVal::Float(lit_val, _) = *lit { + if lit_val < min || lit_val > max { + cx.span_lint(OVERFLOWING_LITERALS, e.span, + &format!("literal out of range for {:?}", t)); } - _ => bug!() - }; - if lit_val < min || lit_val > max { - cx.span_lint(OVERFLOWING_LITERALS, e.span, - &format!("literal out of range for {:?}", t)); } }, _ => () @@ -298,8 +380,8 @@ impl LateLintPass for TypeLimits { fn check_limits(tcx: &TyCtxt, binop: hir::BinOp, l: &hir::Expr, r: &hir::Expr) -> bool { let (lit, expr, swap) = match (&l.node, &r.node) { - (&hir::ExprLit(_), _) => (l, r, true), - (_, &hir::ExprLit(_)) => (r, l, false), + (&hir::ExprLit(ref lit), _) => (lit, r, true), + (_, &hir::ExprLit(ref lit)) => (lit, l, false), _ => return true }; // Normalize the binop so that the literal is always on the RHS in @@ -312,24 +394,25 @@ impl LateLintPass for TypeLimits { match tcx.node_id_to_type(expr.id).sty { ty::TyInt(int_ty) => { let (min, max) = int_ty_range(int_ty); - let lit_val: i64 = match lit.node { - hir::ExprLit(ref li) => match li.node { - ast::LitKind::Int(v, ast::LitIntType::Signed(_)) | - ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i64, - _ => return true + let lit_val: i64 = match *lit { + ConstVal::Integral(ConstInt::InferSigned(v)) => v, + ConstVal::Integral(ConstInt::Infer(v)) => v as i64, + ConstVal::Integral(ConstInt::I8(v)) => v as i64, + ConstVal::Integral(ConstInt::I16(v)) => v as i64, + ConstVal::Integral(ConstInt::I32(v)) => v as i64, + ConstVal::Integral(ConstInt::I64(v)) => v, + ConstVal::Integral(ConstInt::Isize(v)) => { + v.as_i64(tcx.sess.target.int_type) }, - _ => bug!() + _ => return true, }; is_valid(norm_binop, lit_val, min, max) } ty::TyUint(uint_ty) => { let (min, max): (u64, u64) = uint_ty_range(uint_ty); - let lit_val: u64 = match lit.node { - hir::ExprLit(ref li) => match li.node { - ast::LitKind::Int(v, _) => v, - _ => return true - }, - _ => bug!() + let lit_val: u64 = match *lit { + ConstVal::Integral(i) => i.to_u64_unchecked(), + _ => return true, }; is_valid(norm_binop, lit_val, min, max) } @@ -344,16 +427,15 @@ impl LateLintPass for TypeLimits { _ => false } } - - fn forbid_unsigned_negation(cx: &LateContext, span: Span) { - cx.sess() - .struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519") - .span_help(span, "use a cast or the `!` operator") - .emit(); - } } } +fn forbid_unsigned_negation(sess: &Session, span: Span) { + sess.struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519") + .span_help(span, "use a cast or the `!` operator") + .emit(); +} + declare_lint! { IMPROPER_CTYPES, Warn, diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index cabf5c955466c..72fe925b2d87d 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -15,7 +15,7 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use rustc_data_structures::fnv::FnvHashMap; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use rustc::ty::{AdtDef, Ty}; use rustc::mir::repr::*; use hair::*; diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index f70d4321a49b7..fa3b4f72c3b99 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -19,7 +19,7 @@ use build::Builder; use build::matches::{Candidate, MatchPair, Test, TestKind}; use hair::*; use rustc_data_structures::fnv::FnvHashMap; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use rustc::ty::{self, Ty}; use rustc::mir::repr::*; use syntax::codemap::Span; diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index bda9cf058f6e9..91270e5e3384f 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -94,7 +94,7 @@ use rustc::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_val::ConstVal; +use rustc_const_math::ConstVal; use rustc_const_math::ConstInt; pub struct Scope<'tcx> { diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 12dcb32da3fcd..5a4ac8ca07b83 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -16,7 +16,7 @@ use hair::cx::block; use hair::cx::to_ref::ToRef; use rustc::hir::map; use rustc::hir::def::Def; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use rustc_const_eval as const_eval; use rustc::middle::region::CodeExtent; use rustc::hir::pat_util; @@ -246,15 +246,9 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { 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(), - } + ExprKind::Unary { + op: UnOp::Neg, + arg: arg.to_ref(), } } } diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index c3a5fbd967c84..47f1434f7db35 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -18,7 +18,7 @@ use hair::*; use rustc::mir::repr::*; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use rustc_const_eval as const_eval; use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; @@ -91,7 +91,7 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { // All of these contain local IDs, unsuitable for storing in MIR. ConstVal::Struct(_) | ConstVal::Tuple(_) | ConstVal::Array(..) | ConstVal::Repeat(..) | - ConstVal::Function(_) => None, + ConstVal::Function { .. } => None, _ => Some(Literal::Value { value: v }) } diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 51f2cc2687a99..5a8c4a55ed374 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -16,7 +16,7 @@ use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp, TypedConstVal}; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use rustc::hir::def_id::DefId; use rustc::middle::region::CodeExtent; use rustc::ty::subst::Substs; diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 00b8f5c093043..861d2bb20eaa3 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use rustc::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, Pass}; diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 5ce7caf5deb06..e3d2ca59437f6 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -814,13 +814,6 @@ pub fn C_integral(t: Type, u: u64, sign_extend: bool) -> ValueRef { } } -pub fn C_floating(s: &str, t: Type) -> ValueRef { - unsafe { - let s = CString::new(s).unwrap(); - llvm::LLVMConstRealOfString(t.to_ref(), s.as_ptr()) - } -} - pub fn C_floating_f64(f: f64, t: Type) -> ValueRef { unsafe { llvm::LLVMConstReal(t.to_ref(), f) diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index 89f3b295c8d22..a763942cdf00f 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -23,7 +23,7 @@ use base::{self, exported_name, imported_name, push_ctxt}; use callee::Callee; use collector::{self, TransItem}; use common::{type_is_sized, C_nil, const_get_elt}; -use common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty}; +use common::{CrateContext, C_integral, C_floating_f64, C_bool, C_str_slice, C_bytes, val_ty}; use common::{C_struct, C_undef, const_to_opt_int, const_to_opt_uint, VariantInfo, C_uint}; use common::{type_is_fat_ptr, Field, C_vector, C_array, C_null}; use datum::{Datum, Lvalue}; @@ -39,68 +39,74 @@ use rustc::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::cast::{CastTy,IntTy}; use util::nodemap::NodeMap; -use rustc_const_math::{ConstInt, ConstMathErr, ConstUsize, ConstIsize}; +use rustc_const_math::{ConstInt, ConstMathErr, ConstUsize, ConstIsize, ConstVal}; use rustc::hir; use std::ffi::{CStr, CString}; use std::borrow::Cow; use libc::c_uint; -use syntax::ast::{self, LitKind}; +use syntax::ast; use syntax::attr::{self, AttrMetaMethods}; use syntax::parse::token; use syntax::ptr::P; pub type FnArgMap<'a> = Option<&'a NodeMap>; -pub fn const_lit(cx: &CrateContext, e: &hir::Expr, lit: &ast::Lit) +pub fn const_lit(cx: &CrateContext, e: &hir::Expr, lit: &ConstVal) -> ValueRef { let _icx = push_ctxt("trans_lit"); debug!("const_lit: {:?}", lit); - match lit.node { - LitKind::Byte(b) => C_integral(Type::uint_from_ty(cx, ast::UintTy::U8), b as u64, false), - LitKind::Char(i) => C_integral(Type::char(cx), i as u64, false), - LitKind::Int(i, ast::LitIntType::Signed(t)) => { - C_integral(Type::int_from_ty(cx, t), i, true) - } - LitKind::Int(u, ast::LitIntType::Unsigned(t)) => { - C_integral(Type::uint_from_ty(cx, t), u, false) - } - LitKind::Int(i, ast::LitIntType::Unsuffixed) => { - let lit_int_ty = cx.tcx().node_id_to_type(e.id); - match lit_int_ty.sty { - ty::TyInt(t) => { - C_integral(Type::int_from_ty(cx, t), i as u64, true) - } - ty::TyUint(t) => { - C_integral(Type::uint_from_ty(cx, t), i as u64, false) - } - _ => span_bug!(lit.span, - "integer literal has type {:?} (expected int \ - or usize)", - lit_int_ty) + match *lit { + ConstVal::Integral(ConstInt::I8(i)) => C_integral(Type::i8(cx), i as u64, true), + ConstVal::Integral(ConstInt::I16(i)) => C_integral(Type::i16(cx), i as u64, true), + ConstVal::Integral(ConstInt::I32(i)) => C_integral(Type::i32(cx), i as u64, true), + ConstVal::Integral(ConstInt::I64(i)) => C_integral(Type::i64(cx), i as u64, true), + ConstVal::Integral(ConstInt::Isize(i)) => { + let i = i.as_i64(cx.tcx().sess.target.int_type); + C_integral(Type::int(cx), i as u64, true) + }, + ConstVal::Integral(ConstInt::U8(i)) => C_integral(Type::i8(cx), i as u64, false), + ConstVal::Integral(ConstInt::U16(i)) => C_integral(Type::i16(cx), i as u64, false), + ConstVal::Integral(ConstInt::U32(i)) => C_integral(Type::i32(cx), i as u64, false), + ConstVal::Integral(ConstInt::U64(i)) => C_integral(Type::i64(cx), i, false), + ConstVal::Integral(ConstInt::Usize(i)) => { + let u = i.as_u64(cx.tcx().sess.target.uint_type); + C_integral(Type::int(cx), u, false) + }, + ConstVal::Integral(ConstInt::Infer(i)) => { + match cx.tcx().node_id_to_type(e.id).sty { + ty::TyInt(t) => C_integral(Type::int_from_ty(cx, t), i, true), + ty::TyUint(t) => C_integral(Type::uint_from_ty(cx, t), i, false), + _ => span_bug!(e.span, "integer literal has type {:?} (expected int or usize)", + cx.tcx().node_id_to_type(e.id)) } + }, + ConstVal::Integral(ConstInt::InferSigned(i)) => { + match cx.tcx().node_id_to_type(e.id).sty { + ty::TyInt(t) => C_integral(Type::int_from_ty(cx, t), i as u64, true), + _ => span_bug!(e.span, "integer literal has type {:?} (expected int)", + cx.tcx().node_id_to_type(e.id)) + } + }, + ConstVal::Str(ref v) => C_str_slice(cx, v.clone()), + ConstVal::ByteStr(ref v) => addr_of(cx, C_bytes(cx, v), 1, "byte_str"), + ConstVal::Struct(_) | ConstVal::Tuple(_) | + ConstVal::Array(..) | ConstVal::Repeat(..) | + ConstVal::Function { .. } => { + bug!("MIR must not use {:?} (which refers to a local ID)", lit) } - LitKind::Float(ref fs, t) => { - C_floating(&fs, Type::float_from_ty(cx, t)) - } - LitKind::FloatUnsuffixed(ref fs) => { + ConstVal::Char(c) => C_integral(Type::char(cx), c as u64, false), + ConstVal::Float(v, Some(t)) => C_floating_f64(v, Type::float_from_ty(cx, t)), + ConstVal::Float(v, None) => { let lit_float_ty = cx.tcx().node_id_to_type(e.id); match lit_float_ty.sty { - ty::TyFloat(t) => { - C_floating(&fs, Type::float_from_ty(cx, t)) - } - _ => { - span_bug!(lit.span, - "floating point literal doesn't have the right type"); - } + ty::TyFloat(t) => C_floating_f64(v, Type::float_from_ty(cx, t)), + _ => span_bug!(e.span, "floating point literal doesn't have the right type"), } - } - LitKind::Bool(b) => C_bool(cx, b), - LitKind::Str(ref s, _) => C_str_slice(cx, (*s).clone()), - LitKind::ByteStr(ref data) => { - addr_of(cx, C_bytes(cx, &data[..]), 1, "byte_str") - } + }, + ConstVal::Bool(v) => C_bool(cx, v), + ConstVal::Dummy => bug!(), } } @@ -590,7 +596,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, }; let _icx = push_ctxt("const_expr"); Ok(match e.node { - hir::ExprLit(ref lit) => const_lit(cx, e, &lit), + hir::ExprLit(ref lit) => const_lit(cx, e, lit), hir::ExprBinary(b, ref e1, ref e2) => { /* Neither type is bottom, and we expect them to be unified * already, so the following is safe. */ diff --git a/src/librustc_trans/expr.rs b/src/librustc_trans/expr.rs index beca81da05f4c..ad872ddeabe6d 100644 --- a/src/librustc_trans/expr.rs +++ b/src/librustc_trans/expr.rs @@ -82,6 +82,8 @@ use type_::Type; use rustc::hir; +use rustc_const_math::ConstVal; + use syntax::{ast, codemap}; use syntax::parse::token::InternedString; use std::fmt; @@ -675,7 +677,7 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } - hir::ExprLit(ref lit) => trans_immediate_lit(bcx, expr, &lit), + hir::ExprLit(ref lit) => trans_immediate_lit(bcx, expr, lit), hir::ExprBinary(op, ref lhs, ref rhs) => { trans_binary(bcx, expr, op, &lhs, &rhs) } @@ -1103,18 +1105,9 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, dest, expr.debug_loc()) } - hir::ExprLit(ref lit) => { - match lit.node { - ast::LitKind::Str(ref s, _) => { - tvec::trans_lit_str(bcx, expr, (*s).clone(), dest) - } - _ => { - span_bug!(expr.span, - "trans_rvalue_dps_unadjusted shouldn't be \ - translating this type of literal") - } - } - } + hir::ExprLit(ConstVal::Str(ref s)) => tvec::trans_lit_str(bcx, expr, (*s).clone(), dest), + hir::ExprLit(_) => span_bug!(expr.span, "trans_rvalue_dps_unadjusted shouldn't be \ + translating this type of literal"), hir::ExprVec(..) | hir::ExprRepeat(..) => { tvec::trans_fixed_vstore(bcx, expr, dest) } @@ -1459,7 +1452,7 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, fn trans_immediate_lit<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &hir::Expr, - lit: &ast::Lit) + lit: &ConstVal) -> DatumBlock<'blk, 'tcx, Expr> { // must not be a string constant, that is a RvalueDpsExpr let _icx = push_ctxt("trans_immediate_lit"); @@ -2444,14 +2437,11 @@ fn expr_kind(tcx: &TyCtxt, expr: &hir::Expr) -> ExprKind { hir::ExprClosure(..) | hir::ExprBlock(..) | hir::ExprRepeat(..) | + hir::ExprLit(ConstVal::Str(_)) | hir::ExprVec(..) => { ExprKind::RvalueDps } - hir::ExprLit(ref lit) if lit.node.is_str() => { - ExprKind::RvalueDps - } - hir::ExprBreak(..) | hir::ExprAgain(..) | hir::ExprRet(..) | diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index cf85595c08e96..884d2b716862c 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -10,7 +10,7 @@ use llvm::ValueRef; use rustc::ty::{Ty, TypeFoldable}; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use rustc_const_math::ConstInt::*; use rustc_const_eval::lookup_const_by_id; use rustc::mir::repr as mir; @@ -64,7 +64,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let ccx = bcx.ccx(); let llty = type_of::type_of(ccx, ty); match *cv { - ConstVal::Float(v) => C_floating_f64(v, llty), + ConstVal::Float(v, _) => C_floating_f64(v, llty), ConstVal::Bool(v) => C_bool(ccx, v), 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), @@ -82,13 +82,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { 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(Infer(v)) => C_integral(llty, v, 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(_) | ConstVal::Tuple(_) | ConstVal::Array(..) | ConstVal::Repeat(..) | - ConstVal::Function(_) => { + ConstVal::Function { .. } => { bug!("MIR must not use {:?} (which refers to a local ID)", cv) } ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false), diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 8e5d220b4f4e8..d0a68ef81082d 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -11,8 +11,7 @@ use llvm::ValueRef; use rustc::ty::{self, Ty}; use rustc::ty::cast::{CastTy, IntTy}; -use middle::const_val::ConstVal; -use rustc_const_math::ConstInt; +use rustc_const_math::{ConstInt, ConstVal}; use rustc::mir::repr as mir; use asm; diff --git a/src/librustc_trans/tvec.rs b/src/librustc_trans/tvec.rs index f5b9bef5313f2..32d43677d8b2f 100644 --- a/src/librustc_trans/tvec.rs +++ b/src/librustc_trans/tvec.rs @@ -31,8 +31,8 @@ use rustc::ty::{self, Ty}; use rustc::hir; use rustc_const_eval::eval_repeat_count; +use rustc_const_math::ConstVal; -use syntax::ast; use syntax::parse::token::InternedString; #[derive(Copy, Clone, Debug)] @@ -83,15 +83,13 @@ pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let vec_ty = node_id_type(bcx, slice_expr.id); // Handle the "..." case (returns a slice since strings are always unsized): - if let hir::ExprLit(ref lit) = content_expr.node { - if let ast::LitKind::Str(ref s, _) = lit.node { - let scratch = rvalue_scratch_datum(bcx, vec_ty, ""); - bcx = trans_lit_str(bcx, - content_expr, - s.clone(), - SaveIn(scratch.val)); - return DatumBlock::new(bcx, scratch.to_expr_datum()); - } + if let hir::ExprLit(ConstVal::Str(ref s)) = content_expr.node { + let scratch = rvalue_scratch_datum(bcx, vec_ty, ""); + bcx = trans_lit_str(bcx, + content_expr, + s.clone(), + SaveIn(scratch.val)); + return DatumBlock::new(bcx, scratch.to_expr_datum()); } // Handle the &[...] case: @@ -166,27 +164,20 @@ fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, vt, dest, vstore_expr); match content_expr.node { - hir::ExprLit(ref lit) => { - match lit.node { - ast::LitKind::Str(ref s, _) => { - match dest { - Ignore => return bcx, - SaveIn(lldest) => { - let bytes = s.len(); - let llbytes = C_uint(bcx.ccx(), bytes); - let llcstr = C_cstr(bcx.ccx(), (*s).clone(), false); - if !bcx.unreachable.get() { - base::call_memcpy(&B(bcx), lldest, llcstr, llbytes, 1); - } - return bcx; - } + hir::ExprLit(ConstVal::Str(ref s)) => { + match dest { + Ignore => return bcx, + SaveIn(lldest) => { + let bytes = s.len(); + let llbytes = C_uint(bcx.ccx(), bytes); + let llcstr = C_cstr(bcx.ccx(), (*s).clone(), false); + if !bcx.unreachable.get() { + base::call_memcpy(&B(bcx), lldest, llcstr, llbytes, 1); } - } - _ => { - span_bug!(content_expr.span, "unexpected evec content"); + return bcx; } } - } + }, hir::ExprVec(ref elements) => { match dest { Ignore => { @@ -258,14 +249,7 @@ fn elements_required(bcx: Block, content_expr: &hir::Expr) -> usize { //! Figure out the number of elements we need to store this content match content_expr.node { - hir::ExprLit(ref lit) => { - match lit.node { - ast::LitKind::Str(ref s, _) => s.len(), - _ => { - span_bug!(content_expr.span, "unexpected evec content") - } - } - }, + hir::ExprLit(ConstVal::Str(ref s)) => s.len(), hir::ExprVec(ref es) => es.len(), hir::ExprRepeat(_, ref count_expr) => { eval_repeat_count(bcx.tcx(), &count_expr) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 21122e7095dd1..c5448509a6806 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -49,7 +49,6 @@ //! an rptr (`&r.T`) use the region `r` that appears in the rptr. use middle::astconv_util::{prim_ty_to_ty, prohibit_type_params, prohibit_projection}; -use middle::const_val::ConstVal; use rustc_const_eval::eval_const_expr_partial; use rustc_const_eval::EvalHint::UncheckedExprHint; use hir::def::{self, Def}; @@ -66,7 +65,7 @@ use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope, use util::common::{ErrorReported, FN_OUTPUT_NAME}; use util::nodemap::FnvHashSet; -use rustc_const_math::ConstInt; +use rustc_const_math::{ConstInt, ConstVal}; use syntax::{abi, ast}; use syntax::codemap::{Span, Pos}; diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 8dbd6496b6fb0..e38097d40645f 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -33,6 +33,8 @@ use syntax::ptr::P; use rustc::hir::{self, PatKind}; use rustc::hir::print as pprust; +use rustc_const_math::ConstVal; + pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx hir::Pat, expected: Ty<'tcx>) @@ -55,14 +57,12 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically sized byte arrays let mut pat_ty = expr_ty; - if let hir::ExprLit(ref lt) = lt.node { - if let ast::LitKind::ByteStr(_) = lt.node { - let expected_ty = structurally_resolved_type(fcx, pat.span, expected); - if let ty::TyRef(_, mt) = expected_ty.sty { - if let ty::TySlice(_) = mt.ty.sty { - pat_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), - tcx.mk_slice(tcx.types.u8)) - } + if let hir::ExprLit(ConstVal::ByteStr(_)) = lt.node { + let expected_ty = structurally_resolved_type(fcx, pat.span, expected); + if let ty::TyRef(_, mt) = expected_ty.sty { + if let ty::TySlice(_) = mt.ty.sty { + pat_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), + tcx.mk_slice(tcx.types.u8)) } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e7159e65eeb7c..1038e4ec28cd6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -130,6 +130,7 @@ use rustc::hir::{Visibility, PatKind}; use rustc::hir::print as pprust; use rustc_back::slice; use rustc_const_eval::eval_repeat_count; +use rustc_const_math::{ConstVal, ConstInt}; mod assoc; pub mod dropck; @@ -2626,23 +2627,31 @@ fn write_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // AST fragment checking fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - lit: &ast::Lit, + lit: &ConstVal, expected: Expectation<'tcx>) -> Ty<'tcx> { let tcx = fcx.ccx.tcx; - match lit.node { - ast::LitKind::Str(..) => tcx.mk_static_str(), - ast::LitKind::ByteStr(ref v) => { + match *lit { + ConstVal::Str(..) => tcx.mk_static_str(), + ConstVal::ByteStr(ref v) => { tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), tcx.mk_array(tcx.types.u8, v.len())) } - ast::LitKind::Byte(_) => tcx.types.u8, - ast::LitKind::Char(_) => tcx.types.char, - ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t), - ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t), - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { + ConstVal::Char(_) => tcx.types.char, + ConstVal::Integral(ConstInt::I8(_)) => tcx.types.i8, + ConstVal::Integral(ConstInt::I16(_)) => tcx.types.i16, + ConstVal::Integral(ConstInt::I32(_)) => tcx.types.i32, + ConstVal::Integral(ConstInt::I64(_)) => tcx.types.i64, + ConstVal::Integral(ConstInt::Isize(_)) => tcx.types.isize, + ConstVal::Integral(ConstInt::U8(_)) => tcx.types.u8, + ConstVal::Integral(ConstInt::U16(_)) => tcx.types.u16, + ConstVal::Integral(ConstInt::U32(_)) => tcx.types.u32, + ConstVal::Integral(ConstInt::U64(_)) => tcx.types.u64, + ConstVal::Integral(ConstInt::Usize(_)) => tcx.types.usize, + ConstVal::Integral(ConstInt::Infer(_)) | + ConstVal::Integral(ConstInt::InferSigned(_)) => { let opt_ty = expected.to_option(fcx).and_then(|ty| { match ty.sty { ty::TyInt(_) | ty::TyUint(_) => Some(ty), @@ -2655,8 +2664,8 @@ fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, opt_ty.unwrap_or_else( || tcx.mk_int_var(fcx.infcx().next_int_var_id())) } - ast::LitKind::Float(_, t) => tcx.mk_mach_float(t), - ast::LitKind::FloatUnsuffixed(_) => { + ConstVal::Float(_, Some(t)) => tcx.mk_mach_float(t), + ConstVal::Float(_, None) => { let opt_ty = expected.to_option(fcx).and_then(|ty| { match ty.sty { ty::TyFloat(_) => Some(ty), @@ -2666,7 +2675,10 @@ fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, opt_ty.unwrap_or_else( || tcx.mk_float_var(fcx.infcx().next_float_var_id())) } - ast::LitKind::Bool(_) => tcx.types.bool + ConstVal::Bool(_) => tcx.types.bool, + // if a dummy is emitted, an error has already been reported + ConstVal::Dummy => tcx.types.err, + _ => bug!(), } } @@ -3289,7 +3301,7 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } hir::ExprLit(ref lit) => { - let typ = check_lit(fcx, &lit, expected); + let typ = check_lit(fcx, lit, expected); fcx.write_ty(id, typ); } hir::ExprBinary(op, ref lhs, ref rhs) => { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 9d76b4c5284b1..6f1944722bca8 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -66,7 +66,6 @@ use constrained_type_params as ctp; use coherence; use middle::lang_items::SizedTraitLangItem; use middle::resolve_lifetime; -use middle::const_val::ConstVal; use rustc_const_eval::EvalHint::UncheckedExprHint; use rustc_const_eval::eval_const_expr_partial; use rustc::ty::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace}; @@ -82,7 +81,7 @@ use util::common::{ErrorReported, MemoizationMap}; use util::nodemap::{FnvHashMap, FnvHashSet}; use write_ty_to_tcx; -use rustc_const_math::ConstInt; +use rustc_const_math::{ConstInt, ConstVal}; use std::cell::RefCell; use std::collections::HashSet; diff --git a/src/libsyntax/Cargo.toml b/src/libsyntax/Cargo.toml index 964f2dcb6b6b7..245308e026285 100644 --- a/src/libsyntax/Cargo.toml +++ b/src/libsyntax/Cargo.toml @@ -12,3 +12,4 @@ crate-type = ["dylib"] serialize = { path = "../libserialize" } log = { path = "../liblog" } rustc_bitflags = { path = "../librustc_bitflags" } +rustc_const_math = { path = "../librustc_const_math" } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 16d4ed53b5b19..2d7359938ca4e 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -362,6 +362,10 @@ pub trait NodeIdAssigner { fn next_node_id(&self) -> NodeId; fn peek_node_id(&self) -> NodeId; + fn target_bitwidth(&self) -> u32 { + panic!("this ID assigner doesn't know the compilation target's bitwidth"); + } + fn diagnostic(&self) -> &errors::Handler { panic!("this ID assigner cannot emit diagnostics") } diff --git a/src/test/auxiliary/dummy_mir_pass.rs b/src/test/auxiliary/dummy_mir_pass.rs index b5234af937b0c..c19752a34c461 100644 --- a/src/test/auxiliary/dummy_mir_pass.rs +++ b/src/test/auxiliary/dummy_mir_pass.rs @@ -22,7 +22,7 @@ use rustc::mir::transform::{self, MirPass}; use rustc::mir::repr::{Mir, Literal}; use rustc::mir::visit::MutVisitor; use rustc::ty; -use rustc::middle::const_val::ConstVal; +use rustc_const_math::ConstVal; use rustc_const_math::ConstInt; use rustc_plugin::Registry; diff --git a/src/test/compile-fail/feature-gate-negate-unsigned0.rs b/src/test/compile-fail/feature-gate-negate-unsigned0.rs index 05b194345d405..f3ba05ba203ec 100644 --- a/src/test/compile-fail/feature-gate-negate-unsigned0.rs +++ b/src/test/compile-fail/feature-gate-negate-unsigned0.rs @@ -17,15 +17,8 @@ impl std::ops::Neg for S { } 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/feature-gate-negate-unsigned1.rs b/src/test/compile-fail/feature-gate-negate-unsigned1.rs new file mode 100644 index 0000000000000..dfcf84ec798bc --- /dev/null +++ b/src/test/compile-fail/feature-gate-negate-unsigned1.rs @@ -0,0 +1,28 @@ +// 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. + + 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/lint-type-overflow.rs b/src/test/compile-fail/lint-type-overflow.rs index ce336905c0139..dcb294a39edfd 100644 --- a/src/test/compile-fail/lint-type-overflow.rs +++ b/src/test/compile-fail/lint-type-overflow.rs @@ -21,7 +21,6 @@ fn main() { let x1: u8 = 256; //~ error: literal out of range for u8 let x1 = 255_u8; // should be OK - let x1 = 256_u8; //~ error: literal out of range for u8 let x2: i8 = -128; // should be OK let x1: i8 = 128; //~ error: literal out of range for i8 @@ -32,25 +31,17 @@ fn main() { test(1000); //~ error: literal out of range for i8 - let x = 128_i8; //~ error: literal out of range for i8 let x = 127_i8; let x = -128_i8; let x = -(128_i8); - let x = -129_i8; //~ error: literal out of range for i8 let x: i32 = 2147483647; // should be OK let x = 2147483647_i32; // should be OK let x: i32 = 2147483648; //~ error: literal out of range for i32 - let x = 2147483648_i32; //~ error: literal out of range for i32 let x: i32 = -2147483648; // should be OK let x = -2147483648_i32; // should be OK let x: i32 = -2147483649; //~ error: literal out of range for i32 - let x = -2147483649_i32; //~ error: literal out of range for i32 let x = 2147483648; //~ error: literal out of range for i32 - let x = 9223372036854775808_i64; //~ error: literal out of range for i64 let x = -9223372036854775808_i64; // should be OK - 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 } diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs index 83300f18c3e95..96b1c8245ee19 100644 --- a/src/test/compile-fail/lint-type-overflow2.rs +++ b/src/test/compile-fail/lint-type-overflow2.rs @@ -19,4 +19,8 @@ fn main() { 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 + let x: f32 = -3.40282348e+38; //~ error: literal out of range for f32 + let x: f32 = 3.40282348e+38; //~ error: literal out of range for f32 + let x: f64 = -1.7976931348623159e+308; //~ error: literal out of range for f64 + let x: f64 = 1.7976931348623159e+308; //~ error: literal out of range for f64 } diff --git a/src/test/compile-fail/lint-type-overflow_early.rs b/src/test/compile-fail/lint-type-overflow_early.rs new file mode 100644 index 0000000000000..1295053c6d331 --- /dev/null +++ b/src/test/compile-fail/lint-type-overflow_early.rs @@ -0,0 +1,48 @@ +// 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)] + +fn test(x: i8) { + println!("x {}", x); +} + +#[allow(unused_variables)] +fn main() { + let x1: u8 = 255; // should be OK + + let x1 = 255_u8; // should be OK + let x1 = 256_u8; //~ error: literal out of range for u8 + + let x2: i8 = -128; // should be OK + + let x = 128_i8; //~ error: literal out of range for i8 + let x = 127_i8; + let x = -128_i8; + let x = -(128_i8); + let x = -{128_i8}; //~ error: literal out of range for i8 + let x = -129_i8; //~ error: literal out of range for i8 + let x = -(129_i8); //~ error: literal out of range for i8 + let x = -{129_i8}; //~ error: literal out of range for i8 + + let x: i32 = 2147483647; // should be OK + let x = 2147483647_i32; // should be OK + let x = 2147483648_i32; //~ error: literal out of range for i32 + let x: i32 = -2147483648; // should be OK + let x = -2147483648_i32; // should be OK + let x = -2147483649_i32; //~ error: literal out of range for i32 + + let x = 9223372036854775808_i64; //~ error: literal out of range for i64 + let x = -9223372036854775808_i64; // should be OK + let x = 18446744073709551615_i64; //~ error: literal out of range for i64 + let x = -9223372036854775809_i64; //~ error: literal out of range for i64 + let x: i64 = -9223372036854775809; //~ error: literal out of range for i64 +}