about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-04-27 04:00:16 -0700
committerbors <bors@rust-lang.org>2016-04-27 04:00:16 -0700
commitb52d76a08528273b218f168753ed846ecfb59aec (patch)
tree8c036c70947192bfbff3f2147742bac1ba0a8907
parent80bff1eea750404e559887d435c03d21f2b25921 (diff)
parent5cdcad9d357aa59ce0423b4f68cb9386310aba73 (diff)
downloadrust-b52d76a08528273b218f168753ed846ecfb59aec.tar.gz
rust-b52d76a08528273b218f168753ed846ecfb59aec.zip
Auto merge of #33214 - oli-obk:const_err_var_exprs, r=eddyb
report `const_err` on all expressions that can fail

also a drive-by fix for reporting an "overflow in shift *left*" when shifting an `i64` *right*

This increases the warning noise for shifting by more than the bitwidth and for `-T::MIN`. I can silence the bitwidth warnings explicitly and fix the const evaluator to make sure `--$expr` is treated exactly like `$expr` (which is kinda wrong, but mathematically right).

r? @eddyb
-rw-r--r--src/librustc_const_eval/eval.rs83
-rw-r--r--src/librustc_const_math/int.rs2
-rw-r--r--src/librustc_const_math/lib.rs2
-rw-r--r--src/librustc_passes/Cargo.toml1
-rw-r--r--src/librustc_passes/consts.rs50
-rw-r--r--src/librustc_passes/lib.rs1
-rw-r--r--src/test/compile-fail/const-err-early.rs2
-rw-r--r--src/test/compile-fail/const-err.rs6
-rw-r--r--src/test/compile-fail/const-err2.rs34
-rw-r--r--src/test/compile-fail/lint-exceeding-bitshifts.rs1
-rw-r--r--src/test/compile-fail/lint-type-overflow2.rs1
11 files changed, 116 insertions, 67 deletions
diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs
index 3f68f6aecca..c2ac3d838c8 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -562,44 +562,51 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &TyCtxt<'tcx>,
     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))));
-                        },
-                        _ => bug!(),
-                    }
-                },
-                _ => {},
-            }
+        match inner.node {
+            hir::ExprLit(ref lit) => {
+                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))));
+                            },
+                            _ => bug!(),
+                        }
+                    },
+                    _ => {},
+                }
+            },
+            hir::ExprUnary(hir::UnNeg, ref inner) => {
+                // skip `--$expr`
+                return eval_const_expr_partial(tcx, inner, ty_hint, fn_args);
+            },
+            _ => {},
         }
         match eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)? {
           Float(f) => Float(-f),
diff --git a/src/librustc_const_math/int.rs b/src/librustc_const_math/int.rs
index 658d4d9a6d2..64f03be3b5f 100644
--- a/src/librustc_const_math/int.rs
+++ b/src/librustc_const_math/int.rs
@@ -503,7 +503,7 @@ impl ::std::ops::Shr<ConstInt> for ConstInt {
             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))),
+            I64(a) => Ok(I64(overflowing!(a.overflowing_shr(b), Op::Shr))),
             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))),
diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs
index 9f66aac6e38..59792d16e8b 100644
--- a/src/librustc_const_math/lib.rs
+++ b/src/librustc_const_math/lib.rs
@@ -40,4 +40,4 @@ mod err;
 pub use int::*;
 pub use us::*;
 pub use is::*;
-pub use err::ConstMathErr;
+pub use err::{ConstMathErr, Op};
diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml
index fa6bd3dfb67..0c85ffd2e9c 100644
--- a/src/librustc_passes/Cargo.toml
+++ b/src/librustc_passes/Cargo.toml
@@ -12,4 +12,5 @@ crate-type = ["dylib"]
 log = { path = "../liblog" }
 rustc = { path = "../librustc" }
 rustc_const_eval = { path = "../librustc_const_eval" }
+rustc_const_math = { path = "../librustc_const_math" }
 syntax = { path = "../libsyntax" }
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index 2fa7f026a52..1f9c40856fd 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -28,9 +28,11 @@ use rustc::dep_graph::DepNode;
 use rustc::ty::cast::{CastKind};
 use rustc_const_eval::{ConstEvalErr, lookup_const_fn_by_id, compare_lit_exprs};
 use rustc_const_eval::{eval_const_expr_partial, lookup_const_by_id};
-use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal};
-use rustc_const_eval::ErrKind::ErroneousReferencedConstant;
+use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll, Math};
+use rustc_const_eval::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath};
+use rustc_const_eval::ErrKind::UnresolvedPath;
 use rustc_const_eval::EvalHint::ExprTypeChecked;
+use rustc_const_math::{ConstMathErr, Op};
 use rustc::hir::def::Def;
 use rustc::hir::def_id::DefId;
 use rustc::middle::expr_use_visitor as euv;
@@ -437,29 +439,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
                 }
                 intravisit::walk_expr(self, ex);
             }
-            // Division by zero and overflow checking.
-            hir::ExprBinary(op, _, _) => {
-                intravisit::walk_expr(self, ex);
-                let div_or_rem = op.node == hir::BiDiv || op.node == hir::BiRem;
-                match node_ty.sty {
-                    ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
-                        if !self.qualif.intersects(ConstQualif::NOT_CONST) {
-                            match eval_const_expr_partial(
-                                    self.tcx, ex, ExprTypeChecked, None) {
-                                Ok(_) => {}
-                                Err(ConstEvalErr { kind: UnimplementedConstVal(_), ..}) |
-                                Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {},
-                                Err(msg) => {
-                                    self.tcx.sess.add_lint(CONST_ERR, ex.id,
-                                                           msg.span,
-                                                           msg.description().into_owned())
-                                }
-                            }
-                        }
-                    }
-                    _ => {}
-                }
-            }
             _ => intravisit::walk_expr(self, ex)
         }
 
@@ -505,6 +484,27 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
             }
             None => {}
         }
+
+        if self.mode == Mode::Var && !self.qualif.intersects(ConstQualif::NOT_CONST) {
+            match eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) {
+                Ok(_) => {}
+                Err(ConstEvalErr { kind: UnimplementedConstVal(_), ..}) |
+                Err(ConstEvalErr { kind: MiscCatchAll, ..}) |
+                Err(ConstEvalErr { kind: MiscBinaryOp, ..}) |
+                Err(ConstEvalErr { kind: NonConstPath, ..}) |
+                Err(ConstEvalErr { kind: UnresolvedPath, ..}) |
+                Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), ..}) |
+                Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), ..}) |
+                Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), ..}) |
+                Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {},
+                Err(msg) => {
+                    self.tcx.sess.add_lint(CONST_ERR, ex.id,
+                                           msg.span,
+                                           msg.description().into_owned())
+                }
+            }
+        }
+
         self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
         // Don't propagate certain flags.
         self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index 53ae1b30f7c..b235962eb9a 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -30,6 +30,7 @@
 extern crate core;
 #[macro_use] extern crate rustc;
 extern crate rustc_const_eval;
+extern crate rustc_const_math;
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
diff --git a/src/test/compile-fail/const-err-early.rs b/src/test/compile-fail/const-err-early.rs
index cdcdb919bde..7567791c240 100644
--- a/src/test/compile-fail/const-err-early.rs
+++ b/src/test/compile-fail/const-err-early.rs
@@ -18,5 +18,5 @@ pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR attempted to subtract with overfl
 pub const E: u8 = [5u8][1]; //~ ERROR index out of bounds
 
 fn main() {
-    let _e = [6u8][1];
+    let _e = [6u8][1]; //~ ERROR: array index out of bounds
 }
diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs
index 45e8fc37d87..3fb9a3f236c 100644
--- a/src/test/compile-fail/const-err.rs
+++ b/src/test/compile-fail/const-err.rs
@@ -8,8 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// these errors are not actually "const_err", they occur in trans/consts
+// and are unconditional warnings that can't be denied or allowed
+
 #![feature(rustc_attrs)]
 #![allow(exceeding_bitshifts)]
+#![allow(const_err)]
 
 fn black_box<T>(_: T) {
     unimplemented!()
@@ -21,7 +25,7 @@ fn main() {
     //~^ WARN attempted to negate with overflow
     let b = 200u8 + 200u8 + 200u8;
     //~^ WARN attempted to add with overflow
-    //~^^ WARN attempted to add with overflow
+    //~| WARN attempted to add with overflow
     let c = 200u8 * 4;
     //~^ WARN attempted to multiply with overflow
     let d = 42u8 - (42u8 + 1);
diff --git a/src/test/compile-fail/const-err2.rs b/src/test/compile-fail/const-err2.rs
new file mode 100644
index 00000000000..f0d65f1424c
--- /dev/null
+++ b/src/test/compile-fail/const-err2.rs
@@ -0,0 +1,34 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+#![allow(exceeding_bitshifts)]
+#![deny(const_err)]
+
+fn black_box<T>(_: T) {
+    unimplemented!()
+}
+
+fn main() {
+    let a = -std::i8::MIN;
+    //~^ ERROR attempted to negate with overflow
+    let b = 200u8 + 200u8 + 200u8;
+    //~^ ERROR attempted to add with overflow
+    //~| ERROR attempted to add with overflow
+    let c = 200u8 * 4;
+    //~^ ERROR attempted to multiply with overflow
+    let d = 42u8 - (42u8 + 1);
+    //~^ ERROR attempted to subtract with overflow
+    let _e = [5u8][1];
+    black_box(a);
+    black_box(b);
+    black_box(c);
+    black_box(d);
+}
diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs
index e1ed21877c9..6d5abc944e7 100644
--- a/src/test/compile-fail/lint-exceeding-bitshifts.rs
+++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs
@@ -53,6 +53,7 @@ fn main() {
       let n = n << 8; //~ ERROR: bitshift exceeds the type's number of bits
 
       let n = 1u8 << -8; //~ ERROR: bitshift exceeds the type's number of bits
+      //~^ WARN: attempted to shift by a negative amount
 
       let n = 1u8 << (4+3);
       let n = 1u8 << (4+4); //~ ERROR: bitshift exceeds the type's number of bits
diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs
index 83300f18c3e..9499d732a38 100644
--- a/src/test/compile-fail/lint-type-overflow2.rs
+++ b/src/test/compile-fail/lint-type-overflow2.rs
@@ -10,6 +10,7 @@
 //
 
 #![deny(overflowing_literals)]
+#![deny(const_err)]
 
 #[allow(unused_variables)]
 fn main() {