about summary refs log tree commit diff
path: root/src/librustc_const_eval
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-07-27 14:28:06 -0700
committerGitHub <noreply@github.com>2016-07-27 14:28:06 -0700
commitf2e59cc6aa3bf901a4aefa8ad4954edddf3da29f (patch)
tree7a0adacdd7888b935b3cb03c2851b7631c1d869b /src/librustc_const_eval
parent29abe5ec717bd58b175100cfabe833c1500c8498 (diff)
parent717e39294f635d90f8ba9e0968494f741878f37b (diff)
downloadrust-f2e59cc6aa3bf901a4aefa8ad4954edddf3da29f.tar.gz
rust-f2e59cc6aa3bf901a4aefa8ad4954edddf3da29f.zip
Auto merge of #34907 - arielb1:found-parse-error, r=nikomatsakis
Centralize and clean type error reporting

Refactors the code that handles type errors to be cleaner and fixes various edge cases.

This made the already-bad "type mismatch resolving" error message somewhat uglier. I want to fix that in another commit before this PR is merged.

Fixes #31173

r? @jonathandturner, cc @nikomatsakis
Diffstat (limited to 'src/librustc_const_eval')
-rw-r--r--src/librustc_const_eval/Cargo.toml1
-rw-r--r--src/librustc_const_eval/check_match.rs55
-rw-r--r--src/librustc_const_eval/diagnostics.rs42
-rw-r--r--src/librustc_const_eval/eval.rs232
-rw-r--r--src/librustc_const_eval/lib.rs1
5 files changed, 212 insertions, 119 deletions
diff --git a/src/librustc_const_eval/Cargo.toml b/src/librustc_const_eval/Cargo.toml
index 01872bbe3c0..8967672548b 100644
--- a/src/librustc_const_eval/Cargo.toml
+++ b/src/librustc_const_eval/Cargo.toml
@@ -14,6 +14,7 @@ serialize = { path = "../libserialize" }
 rustc = { path = "../librustc" }
 rustc_back = { path = "../librustc_back" }
 rustc_const_math = { path = "../librustc_const_math" }
+rustc_errors = { path = "../librustc_errors" }
 syntax = { path = "../libsyntax" }
 graphviz = { path = "../libgraphviz" }
 syntax_pos = { path = "../libsyntax_pos" }
\ No newline at end of file
diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs
index 0de00d9d7f6..915a0cf0bdc 100644
--- a/src/librustc_const_eval/check_match.rs
+++ b/src/librustc_const_eval/check_match.rs
@@ -17,6 +17,7 @@ use rustc::middle::const_val::ConstVal;
 use ::{eval_const_expr, eval_const_expr_partial, compare_const_vals};
 use ::{const_expr_to_pat, lookup_const_by_id};
 use ::EvalHint::ExprTypeChecked;
+use eval::report_const_eval_err;
 use rustc::hir::def::*;
 use rustc::hir::def_id::{DefId};
 use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor};
@@ -42,6 +43,7 @@ use syntax_pos::{Span, DUMMY_SP};
 use rustc::hir::fold::{Folder, noop_fold_pat};
 use rustc::hir::print::pat_to_string;
 use syntax::ptr::P;
+use rustc::util::common::ErrorReported;
 use rustc::util::nodemap::FnvHashMap;
 
 pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
@@ -279,13 +281,7 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
                 Ok(_) => {}
 
                 Err(err) => {
-                    let mut diag = struct_span_err!(cx.tcx.sess, err.span, E0471,
-                                                    "constant evaluation error: {}",
-                                                    err.description());
-                    if !p.span.contains(err.span) {
-                        diag.span_note(p.span, "in pattern here");
-                    }
-                    diag.emit();
+                    report_const_eval_err(cx.tcx, &err, p.span, "pattern").emit();
                 }
             }
         }
@@ -838,22 +834,19 @@ pub fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> us
     }
 }
 
-fn range_covered_by_constructor(ctor: &Constructor,
-                                from: &ConstVal, to: &ConstVal) -> Option<bool> {
+fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
+                                ctor: &Constructor,
+                                from: &ConstVal, to: &ConstVal)
+                                -> Result<bool, ErrorReported> {
     let (c_from, c_to) = match *ctor {
         ConstantValue(ref value)        => (value, value),
         ConstantRange(ref from, ref to) => (from, to),
-        Single                          => return Some(true),
+        Single                          => return Ok(true),
         _                               => bug!()
     };
-    let cmp_from = compare_const_vals(c_from, from);
-    let cmp_to = compare_const_vals(c_to, to);
-    match (cmp_from, cmp_to) {
-        (Some(cmp_from), Some(cmp_to)) => {
-            Some(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
-        }
-        _ => None
-    }
+    let cmp_from = compare_const_vals(tcx, span, c_from, from)?;
+    let cmp_to = compare_const_vals(tcx, span, c_to, to)?;
+    Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
 }
 
 fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>,
@@ -965,13 +958,12 @@ pub fn specialize<'a, 'b, 'tcx>(
                 Some(vec![(pat, Some(mt.ty))])
             } else {
                 let expr_value = eval_const_expr(cx.tcx, &expr);
-                match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
-                    Some(true) => Some(vec![]),
-                    Some(false) => None,
-                    None => {
-                        span_err!(cx.tcx.sess, pat_span, E0298, "mismatched types between arms");
-                        None
-                    }
+                match range_covered_by_constructor(
+                    cx.tcx, expr.span, constructor, &expr_value, &expr_value
+                ) {
+                    Ok(true) => Some(vec![]),
+                    Ok(false) => None,
+                    Err(ErrorReported) => None,
                 }
             }
         }
@@ -979,13 +971,12 @@ pub fn specialize<'a, 'b, 'tcx>(
         PatKind::Range(ref from, ref to) => {
             let from_value = eval_const_expr(cx.tcx, &from);
             let to_value = eval_const_expr(cx.tcx, &to);
-            match range_covered_by_constructor(constructor, &from_value, &to_value) {
-                Some(true) => Some(vec![]),
-                Some(false) => None,
-                None => {
-                    span_err!(cx.tcx.sess, pat_span, E0299, "mismatched types between arms");
-                    None
-                }
+            match range_covered_by_constructor(
+                cx.tcx, pat_span, constructor, &from_value, &to_value
+            ) {
+                Ok(true) => Some(vec![]),
+                Ok(false) => None,
+                Err(ErrorReported) => None,
             }
         }
 
diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs
index f2abdf831a3..45414c33c07 100644
--- a/src/librustc_const_eval/diagnostics.rs
+++ b/src/librustc_const_eval/diagnostics.rs
@@ -551,44 +551,46 @@ The `op_string_ref` binding has type `&Option<&String>` in both cases.
 See also https://github.com/rust-lang/rust/issues/14587
 "##,
 
-E0306: r##"
-In an array literal `[x; N]`, `N` is the number of elements in the array. This
-must be an unsigned integer. Erroneous code example:
+E0080: r##"
+This error indicates that the compiler was unable to sensibly evaluate an
+constant expression that had to be evaluated. Attempting to divide by 0
+or causing integer overflow are two ways to induce this error. For example:
 
 ```compile_fail
-let x = [0i32; true]; // error: expected positive integer for repeat count,
-                      //        found boolean
+enum Enum {
+    X = (1 << 500),
+    Y = (1 / 0)
+}
 ```
 
-Working example:
+Ensure that the expressions given can be evaluated as the desired integer type.
+See the FFI section of the Reference for more information about using a custom
+integer type:
 
-```
-let x = [0i32; 2];
-```
+https://doc.rust-lang.org/reference.html#ffi-attributes
 "##,
 
-E0307: r##"
-The length of an array is part of its type. For this reason, this length must
-be a compile-time constant. Erroneous code example:
+
+E0306: r##"
+In an array literal `[x; N]`, `N` is the number of elements in the array. This
+must be an unsigned integer. Erroneous code example:
 
 ```compile_fail
-    let len = 10;
-    let x = [0i32; len]; // error: expected constant integer for repeat count,
-                         //        found variable
+let x = [0i32; true]; // error: expected positive integer for repeat count,
+                      //        found boolean
 ```
 
 Working example:
 
 ```
-let x = [0i32; 10];
+let x = [0i32; 2];
 ```
 "##,
-
 }
 
 
 register_diagnostics! {
-E0298, // mismatched types between arms
-E0299, // mismatched types between arms
-E0471, // constant evaluation error: ..
+    E0298, // cannot compare constants
+//  E0299, // mismatched types between arms
+//  E0471, // constant evaluation error (in pattern)
 }
diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs
index 4643686786b..dd21bb17a2d 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -25,6 +25,7 @@ use rustc::hir::pat_util::def_to_path;
 use rustc::ty::{self, Ty, TyCtxt, subst};
 use rustc::ty::util::IntTypeExt;
 use rustc::traits::ProjectionMode;
+use rustc::util::common::ErrorReported;
 use rustc::util::nodemap::NodeMap;
 use rustc::lint;
 
@@ -43,6 +44,7 @@ use std::cmp::Ordering;
 use std::collections::hash_map::Entry::Vacant;
 
 use rustc_const_math::*;
+use rustc_errors::{DiagnosticBuilder, check_old_school};
 
 macro_rules! math {
     ($e:expr, $op:expr) => {
@@ -338,20 +340,71 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     Ok(P(hir::Pat { id: expr.id, node: pat, span: span }))
 }
 
+pub fn report_const_eval_err<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    err: &ConstEvalErr,
+    primary_span: Span,
+    primary_kind: &str)
+    -> DiagnosticBuilder<'tcx>
+{
+    let mut err = err;
+    while let &ConstEvalErr { kind: ErroneousReferencedConstant(box ref i_err), .. } = err {
+        err = i_err;
+    }
+
+    let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error");
+    note_const_eval_err(tcx, err, primary_span, primary_kind, &mut diag);
+    diag
+}
+
+pub fn fatal_const_eval_err<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    err: &ConstEvalErr,
+    primary_span: Span,
+    primary_kind: &str)
+    -> !
+{
+    report_const_eval_err(tcx, err, primary_span, primary_kind).emit();
+    tcx.sess.abort_if_errors();
+    unreachable!()
+}
+
+pub fn note_const_eval_err<'a, 'tcx>(
+    _tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    err: &ConstEvalErr,
+    primary_span: Span,
+    primary_kind: &str,
+    diag: &mut DiagnosticBuilder)
+{
+    match err.description() {
+        ConstEvalErrDescription::Simple(message) => {
+            if check_old_school() {
+                diag.note(&message);
+            } else {
+                diag.span_label(err.span, &message);
+            }
+        }
+    }
+
+    if !primary_span.contains(err.span) {
+        diag.span_note(primary_span,
+                       &format!("for {} here", primary_kind));
+    }
+}
+
 pub fn eval_const_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                  e: &Expr) -> ConstVal {
     match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) {
         Ok(r) => r,
         // non-const path still needs to be a fatal error, because enums are funky
         Err(s) => {
+            report_const_eval_err(tcx, &s, e.span, "expression").emit();
             match s.kind {
                 NonConstPath |
-                UnimplementedConstVal(_) => tcx.sess.span_fatal(s.span, &s.description()),
-                _ => {
-                    tcx.sess.span_err(s.span, &s.description());
-                    Dummy
-                }
+                UnimplementedConstVal(_) => tcx.sess.abort_if_errors(),
+                _ => {}
             }
+            Dummy
         },
     }
 }
@@ -400,6 +453,7 @@ pub enum ErrKind {
     IntermediateUnsignedNegative,
     /// Expected, Got
     TypeMismatch(String, ConstInt),
+
     BadType(ConstVal),
     ErroneousReferencedConstant(Box<ConstEvalErr>),
     CharCast(ConstInt),
@@ -411,57 +465,79 @@ impl From<ConstMathErr> for ErrKind {
     }
 }
 
+#[derive(Clone, Debug)]
+pub enum ConstEvalErrDescription<'a> {
+    Simple(Cow<'a, str>),
+}
+
+impl<'a> ConstEvalErrDescription<'a> {
+    /// Return a one-line description of the error, for lints and such
+    pub fn into_oneline(self) -> Cow<'a, str> {
+        match self {
+            ConstEvalErrDescription::Simple(simple) => simple,
+        }
+    }
+}
+
 impl ConstEvalErr {
-    pub fn description(&self) -> Cow<str> {
+    pub fn description(&self) -> ConstEvalErrDescription {
         use self::ErrKind::*;
+        use self::ConstEvalErrDescription::*;
+
+        macro_rules! simple {
+            ($msg:expr) => ({ Simple($msg.into_cow()) });
+            ($fmt:expr, $($arg:tt)+) => ({
+                Simple(format!($fmt, $($arg)+).into_cow())
+            })
+        }
 
         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 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(),
-            InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(),
-            NegateOn(ref const_val) => format!("negate on {}", const_val.description()).into_cow(),
-            NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(),
-            CallOn(ref const_val) => format!("call on {}", const_val.description()).into_cow(),
-
-            MissingStructField  => "nonexistent struct field".into_cow(),
-            NonConstPath        => "non-constant path in constant expression".into_cow(),
+            CannotCast => simple!("can't cast this type"),
+            CannotCastTo(s) => simple!("can't cast this type to {}", s),
+            InvalidOpForInts(_) =>  simple!("can't do this op on integrals"),
+            InvalidOpForBools(_) =>  simple!("can't do this op on bools"),
+            InvalidOpForFloats(_) => simple!("can't do this op on floats"),
+            InvalidOpForIntUint(..) => simple!("can't do this op on an isize and usize"),
+            InvalidOpForUintInt(..) => simple!("can't do this op on a usize and isize"),
+            NegateOn(ref const_val) => simple!("negate on {}", const_val.description()),
+            NotOn(ref const_val) => simple!("not on {}", const_val.description()),
+            CallOn(ref const_val) => simple!("call on {}", const_val.description()),
+
+            MissingStructField  => simple!("nonexistent struct field"),
+            NonConstPath        => simple!("non-constant path in constant expression"),
             UnimplementedConstVal(what) =>
-                format!("unimplemented constant expression: {}", what).into_cow(),
-            UnresolvedPath => "unresolved path in constant expression".into_cow(),
-            ExpectedConstTuple => "expected constant tuple".into_cow(),
-            ExpectedConstStruct => "expected constant struct".into_cow(),
-            TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(),
-            IndexedNonVec => "indexing is only supported for arrays".into_cow(),
-            IndexNegative => "indices must be non-negative integers".into_cow(),
-            IndexNotInt => "indices must be integers".into_cow(),
+                simple!("unimplemented constant expression: {}", what),
+            UnresolvedPath => simple!("unresolved path in constant expression"),
+            ExpectedConstTuple => simple!("expected constant tuple"),
+            ExpectedConstStruct => simple!("expected constant struct"),
+            TupleIndexOutOfBounds => simple!("tuple index out of bounds"),
+            IndexedNonVec => simple!("indexing is only supported for arrays"),
+            IndexNegative => simple!("indices must be non-negative integers"),
+            IndexNotInt => simple!("indices must be integers"),
             IndexOutOfBounds { len, index } => {
-                format!("index out of bounds: the len is {} but the index is {}",
-                        len, index).into_cow()
+                simple!("index out of bounds: the len is {} but the index is {}",
+                        len, index)
             }
-            RepeatCountNotNatural => "repeat count must be a natural number".into_cow(),
-            RepeatCountNotInt => "repeat count must be integers".into_cow(),
+            RepeatCountNotNatural => simple!("repeat count must be a natural number"),
+            RepeatCountNotInt => simple!("repeat count must be integers"),
 
-            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(),
+            MiscBinaryOp => simple!("bad operands for binary"),
+            MiscCatchAll => simple!("unsupported constant expr"),
+            IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
+            Math(ref err) => Simple(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(),
+            IntermediateUnsignedNegative => simple!(
+                "during the computation of an unsigned a negative \
+                 number was encountered. This is most likely a bug in\
+                 the constant evaluator"),
 
             TypeMismatch(ref expected, ref got) => {
-                format!("mismatched types: expected `{}`, found `{}`",
-                        expected, got.description()).into_cow()
+                simple!("expected {}, found {}", expected, got.description())
             },
-            BadType(ref i) => format!("value of wrong type: {:?}", i).into_cow(),
-            ErroneousReferencedConstant(_) => "could not evaluate referenced constant".into_cow(),
+            BadType(ref i) => simple!("value of wrong type: {:?}", i),
+            ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),
             CharCast(ref got) => {
-                format!("only `u8` can be cast as `char`, not `{}`", got.description()).into_cow()
+                simple!("only `u8` can be cast as `char`, not `{}`", got.description())
             },
         }
     }
@@ -1199,8 +1275,10 @@ fn parse_float(num: &str, fty_hint: Option<ast::FloatTy>, span: Span) -> ConstFl
     })
 }
 
-pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
-    match (a, b) {
+pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal)
+                          -> Result<Ordering, ErrorReported>
+{
+    let result = match (a, b) {
         (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
         (&Float(a), &Float(b)) => a.try_cmp(b).ok(),
         (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)),
@@ -1208,62 +1286,82 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
         (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)),
         (&Char(a), &Char(ref b)) => Some(a.cmp(b)),
         _ => None,
+    };
+
+    match result {
+        Some(result) => Ok(result),
+        None => {
+            // FIXME: can this ever be reached?
+            span_err!(tcx.sess, span, E0298,
+                      "type mismatch comparing {} and {}",
+                      a.description(),
+                      b.description());
+            Err(ErrorReported)
+        }
     }
 }
 
 pub fn compare_lit_exprs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                   span: Span,
                                    a: &Expr,
-                                   b: &Expr) -> Option<Ordering> {
+                                   b: &Expr) -> Result<Ordering, ErrorReported> {
     let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) {
         Ok(a) => a,
         Err(e) => {
-            tcx.sess.span_err(a.span, &e.description());
-            return None;
+            report_const_eval_err(tcx, &e, a.span, "expression").emit();
+            return Err(ErrorReported);
         }
     };
     let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) {
         Ok(b) => b,
         Err(e) => {
-            tcx.sess.span_err(b.span, &e.description());
-            return None;
+            report_const_eval_err(tcx, &e, b.span, "expression").emit();
+            return Err(ErrorReported);
         }
     };
-    compare_const_vals(&a, &b)
+    compare_const_vals(tcx, span, &a, &b)
 }
 
 
-/// Returns the repeat count for a repeating vector expression.
-pub fn eval_repeat_count<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                   count_expr: &hir::Expr) -> usize {
+/// Returns the value of the length-valued expression
+pub fn eval_length<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                             count_expr: &hir::Expr,
+                             reason: &str)
+                             -> Result<usize, ErrorReported>
+{
     let hint = UncheckedExprHint(tcx.types.usize);
     match eval_const_expr_partial(tcx, count_expr, hint, None) {
         Ok(Integral(Usize(count))) => {
             let val = count.as_u64(tcx.sess.target.uint_type);
             assert_eq!(val as usize as u64, val);
-            val as usize
+            Ok(val as usize)
         },
         Ok(const_val) => {
             span_err!(tcx.sess, count_expr.span, E0306,
-                      "expected positive integer for repeat count, found {}",
+                      "expected usize for {}, found {}",
+                      reason,
                       const_val.description());
-            0
+            Err(ErrorReported)
         }
         Err(err) => {
-            let err_msg = match count_expr.node {
+            let mut diag = report_const_eval_err(
+                tcx, &err, count_expr.span, reason);
+
+            match count_expr.node {
                 hir::ExprPath(None, hir::Path {
                     global: false,
                     ref segments,
                     ..
-                }) if segments.len() == 1 =>
-                    format!("found variable"),
-                _ => match err.kind {
-                    MiscCatchAll => format!("but found {}", err.description()),
-                    _ => format!("but {}", err.description())
+                }) if segments.len() == 1 => {
+                    if let Some(Def::Local(..)) = tcx.expect_def_or_none(count_expr.id) {
+                        diag.note(&format!("`{}` is a variable", segments[0].name));
+                    }
                 }
-            };
-            span_err!(tcx.sess, count_expr.span, E0307,
-                "expected constant integer for repeat count, {}", err_msg);
-            0
+                _ => {}
+            }
+
+            diag.emit();
+            Err(ErrorReported)
         }
     }
 }
diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs
index 726ba4fc192..a6714c178e7 100644
--- a/src/librustc_const_eval/lib.rs
+++ b/src/librustc_const_eval/lib.rs
@@ -36,6 +36,7 @@
 #[macro_use] extern crate rustc;
 extern crate rustc_back;
 extern crate rustc_const_math;
+extern crate rustc_errors;
 extern crate graphviz;
 extern crate syntax_pos;
 extern crate serialize as rustc_serialize; // used by deriving