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
This commit is contained in:
bors 2016-07-27 14:28:06 -07:00 committed by GitHub
commit f2e59cc6aa
85 changed files with 1017 additions and 844 deletions

View file

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

View file

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

View file

@ -551,6 +551,26 @@ The `op_string_ref` binding has type `&Option<&String>` in both cases.
See also https://github.com/rust-lang/rust/issues/14587
"##,
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
enum Enum {
X = (1 << 500),
Y = (1 / 0)
}
```
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:
https://doc.rust-lang.org/reference.html#ffi-attributes
"##,
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:
@ -566,29 +586,11 @@ Working example:
let x = [0i32; 2];
```
"##,
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:
```compile_fail
let len = 10;
let x = [0i32; len]; // error: expected constant integer for repeat count,
// found variable
```
Working example:
```
let x = [0i32; 10];
```
"##,
}
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)
}

View file

@ -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(),
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 => "nonexistent struct field".into_cow(),
NonConstPath => "non-constant path in constant expression".into_cow(),
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)
}
}
}

View file

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