Skip to content

Commit

Permalink
Rollup merge of rust-lang#51316 - oli-obk:const_err, r=nikomatsakis
Browse files Browse the repository at this point in the history
Refactor the const eval diagnostic API

* no longer report "const eval error" for things that have typeck errors
* errors and lints have saner spans and messages
* unified the diagnostic logic (const eval errors were slightly different depending on where they were reported, and there was also code duplication between the different reporters)
* report errors if an erroneous constant is used inside a promoted (fixes most of rust-lang#50814)
  • Loading branch information
Mark-Simulacrum committed Jun 5, 2018
2 parents 26ad9e2 + 14d08e5 commit 7d0d7ea
Show file tree
Hide file tree
Showing 65 changed files with 882 additions and 462 deletions.
17 changes: 4 additions & 13 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ impl_stable_hash_for!(struct ::middle::const_val::ConstEvalErr<'tcx> {

impl_stable_hash_for!(struct ::middle::const_val::FrameInfo {
span,
lint_root,
location
});

Expand All @@ -523,21 +524,11 @@ for ::middle::const_val::ErrKind<'gcx> {
mem::discriminant(self).hash_stable(hcx, hasher);

match *self {
NonConstPath |
TypeckError |
CouldNotResolve |
CheckMatchError => {
// nothing to do
}
UnimplementedConstVal(s) => {
s.hash_stable(hcx, hasher);
}
IndexOutOfBounds { len, index } => {
len.hash_stable(hcx, hasher);
index.hash_stable(hcx, hasher);
}
LayoutError(ref layout_error) => {
layout_error.hash_stable(hcx, hasher);
}
Miri(ref err, ref trace) => {
err.hash_stable(hcx, hasher);
trace.hash_stable(hcx, hasher);
Expand Down Expand Up @@ -608,8 +599,8 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
RemainderByZero |
DivisionByZero |
GeneratorResumedAfterReturn |
GeneratorResumedAfterPanic |
ReferencedConstant => {}
GeneratorResumedAfterPanic => {}
ReferencedConstant(ref err) => err.hash_stable(hcx, hasher),
MachineError(ref err) => err.hash_stable(hcx, hasher),
FunctionPointerTyMismatch(a, b) => {
a.hash_stable(hcx, hasher);
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/lint/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,9 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> {
f(self);
self.param_env = old_param_env;
}
pub fn current_lint_root(&self) -> ast::NodeId {
self.last_ast_node_with_lint_attrs
}
}

impl<'a, 'tcx> LayoutOf for &'a LateContext<'a, 'tcx> {
Expand Down
139 changes: 77 additions & 62 deletions src/librustc/middle/const_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
// except according to those terms.

use hir::def_id::DefId;
use ty::{self, TyCtxt, layout};
use ty;
use ty::subst::Substs;
use ty::maps::TyCtxtAt;
use mir::interpret::ConstValue;
use errors::DiagnosticBuilder;

use graphviz::IntoCow;
use syntax_pos::Span;
use syntax::ast;

use std::borrow::Cow;
use rustc_data_structures::sync::Lrc;
Expand All @@ -28,30 +30,26 @@ pub enum ConstVal<'tcx> {
Value(ConstValue<'tcx>),
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ConstEvalErr<'tcx> {
pub span: Span,
pub kind: Lrc<ErrKind<'tcx>>,
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum ErrKind<'tcx> {

NonConstPath,
UnimplementedConstVal(&'static str),
IndexOutOfBounds { len: u64, index: u64 },

LayoutError(layout::LayoutError<'tcx>),

CouldNotResolve,
TypeckError,
CheckMatchError,
Miri(::mir::interpret::EvalError<'tcx>, Vec<FrameInfo>),
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct FrameInfo {
pub span: Span,
pub location: String,
pub lint_root: Option<ast::NodeId>,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -83,81 +81,98 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
}

match *self.kind {
NonConstPath => simple!("non-constant path in constant expression"),
UnimplementedConstVal(what) =>
simple!("unimplemented constant expression: {}", what),
IndexOutOfBounds { len, index } => {
simple!("index out of bounds: the len is {} but the index is {}",
len, index)
}

LayoutError(ref err) => Simple(err.to_string().into_cow()),

CouldNotResolve => simple!("could not resolve"),
TypeckError => simple!("type-checking failed"),
CheckMatchError => simple!("match-checking failed"),
Miri(ref err, ref trace) => Backtrace(err, trace),
}
}

pub fn struct_error(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
primary_span: Span,
primary_kind: &str)
-> DiagnosticBuilder<'gcx>
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
message: &str)
-> Option<DiagnosticBuilder<'tcx>>
{
let mut diag = struct_error(tcx, self.span, "constant evaluation error");
self.note(tcx, primary_span, primary_kind, &mut diag);
diag
self.struct_generic(tcx, message, None, true)
}

pub fn note(&self,
_tcx: TyCtxt<'a, 'gcx, 'tcx>,
primary_span: Span,
primary_kind: &str,
diag: &mut DiagnosticBuilder)
{
match self.description() {
ConstEvalErrDescription::Simple(message) => {
diag.span_label(self.span, message);
}
ConstEvalErrDescription::Backtrace(miri, frames) => {
diag.span_label(self.span, format!("{}", miri));
for frame in frames {
diag.span_label(frame.span, format!("inside call to `{}`", frame.location));
}
}
pub fn report_as_error(&self,
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
message: &str
) {
let err = self.struct_generic(tcx, message, None, true);
if let Some(mut err) = err {
err.emit();
}
}

if !primary_span.contains(self.span) {
diag.span_note(primary_span,
&format!("for {} here", primary_kind));
pub fn report_as_lint(&self,
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
message: &str,
lint_root: ast::NodeId,
) {
let lint = self.struct_generic(
tcx,
message,
Some(lint_root),
false,
);
if let Some(mut lint) = lint {
lint.emit();
}
}

pub fn report(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
primary_span: Span,
primary_kind: &str)
{
match *self.kind {
ErrKind::TypeckError | ErrKind::CheckMatchError => return,
ErrKind::Miri(ref miri, _) => {
fn struct_generic(
&self,
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
message: &str,
lint_root: Option<ast::NodeId>,
as_err: bool,
) -> Option<DiagnosticBuilder<'tcx>> {
let (msg, frames): (_, &[_]) = match *self.kind {
ErrKind::TypeckError | ErrKind::CheckMatchError => return None,
ErrKind::Miri(ref miri, ref frames) => {
match miri.kind {
::mir::interpret::EvalErrorKind::TypeckError |
::mir::interpret::EvalErrorKind::Layout(_) => return,
_ => {},
::mir::interpret::EvalErrorKind::Layout(_) => return None,
::mir::interpret::EvalErrorKind::ReferencedConstant(ref inner) => {
inner.struct_generic(tcx, "referenced constant", lint_root, as_err)?.emit();
(miri.to_string(), frames)
},
_ => (miri.to_string(), frames),
}
}
_ => {}
_ => (self.description().into_oneline().to_string(), &[]),
};
trace!("reporting const eval failure at {:?}", self.span);
let mut err = if as_err {
struct_error(tcx, message)
} else {
let node_id = frames
.iter()
.rev()
.filter_map(|frame| frame.lint_root)
.next()
.or(lint_root)
.expect("some part of a failing const eval must be local");
tcx.struct_span_lint_node(
::rustc::lint::builtin::CONST_ERR,
node_id,
tcx.span,
message,
)
};
err.span_label(self.span, msg);
for FrameInfo { span, location, .. } in frames {
err.span_label(*span, format!("inside call to `{}`", location));
}
self.struct_error(tcx, primary_span, primary_kind).emit();
Some(err)
}
}

pub fn struct_error<'a, 'gcx, 'tcx>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
span: Span,
tcx: TyCtxtAt<'a, 'gcx, 'tcx>,
msg: &str,
) -> DiagnosticBuilder<'gcx> {
struct_span_err!(tcx.sess, span, E0080, "{}", msg)
) -> DiagnosticBuilder<'tcx> {
struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
}
48 changes: 39 additions & 9 deletions src/librustc/mir/interpret/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{fmt, env};

use mir;
use middle::const_val::ConstEvalErr;
use ty::{FnSig, Ty, layout};
use ty::layout::{Size, Align};

Expand All @@ -10,21 +11,50 @@ use super::{

use backtrace::Backtrace;

#[derive(Debug, Clone)]
#[derive(Debug, Clone, RustcEncodable, RustcDecodable)]
pub struct EvalError<'tcx> {
pub kind: EvalErrorKind<'tcx, u64>,
pub backtrace: Option<Backtrace>,
}

impl<'tcx> From<EvalErrorKind<'tcx, u64>> for EvalError<'tcx> {
fn from(kind: EvalErrorKind<'tcx, u64>) -> Self {
let backtrace = match env::var("MIRI_BACKTRACE") {
Ok(ref val) if !val.is_empty() => Some(Backtrace::new_unresolved()),
_ => None
};
match env::var("MIRI_BACKTRACE") {
Ok(ref val) if !val.is_empty() => {
let backtrace = Backtrace::new();

use std::fmt::Write;
let mut trace_text = "\n\nAn error occurred in miri:\n".to_string();
write!(trace_text, "backtrace frames: {}\n", backtrace.frames().len()).unwrap();
'frames: for (i, frame) in backtrace.frames().iter().enumerate() {
if frame.symbols().is_empty() {
write!(trace_text, "{}: no symbols\n", i).unwrap();
}
for symbol in frame.symbols() {
write!(trace_text, "{}: ", i).unwrap();
if let Some(name) = symbol.name() {
write!(trace_text, "{}\n", name).unwrap();
} else {
write!(trace_text, "<unknown>\n").unwrap();
}
write!(trace_text, "\tat ").unwrap();
if let Some(file_path) = symbol.filename() {
write!(trace_text, "{}", file_path.display()).unwrap();
} else {
write!(trace_text, "<unknown_file>").unwrap();
}
if let Some(line) = symbol.lineno() {
write!(trace_text, ":{}\n", line).unwrap();
} else {
write!(trace_text, "\n").unwrap();
}
}
}
error!("{}", trace_text);
},
_ => {},
}
EvalError {
kind,
backtrace,
}
}
}
Expand Down Expand Up @@ -122,7 +152,7 @@ pub enum EvalErrorKind<'tcx, O> {
TypeckError,
/// Cannot compute this constant because it depends on another one
/// which already produced an error
ReferencedConstant,
ReferencedConstant(ConstEvalErr<'tcx>),
GeneratorResumedAfterReturn,
GeneratorResumedAfterPanic,
}
Expand Down Expand Up @@ -238,7 +268,7 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
"there were unresolved type arguments during trait selection",
TypeckError =>
"encountered constants with type errors, stopping evaluation",
ReferencedConstant =>
ReferencedConstant(_) =>
"referenced constant has errors",
Overflow(mir::BinOp::Add) => "attempt to add with overflow",
Overflow(mir::BinOp::Sub) => "attempt to subtract with overflow",
Expand Down
9 changes: 6 additions & 3 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,10 +827,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}

ConstEvalFailure(ref err) => {
if let ::middle::const_val::ErrKind::TypeckError = *err.kind {
return;
match err.struct_error(
self.tcx.at(span),
"could not evaluate constant expression",
) {
Some(err) => err,
None => return,
}
err.struct_error(self.tcx, span, "constant expression")
}

Overflow => {
Expand Down
3 changes: 1 addition & 2 deletions src/librustc/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,7 @@ fn process_predicate<'a, 'gcx, 'tcx>(
} else {
Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr {
span: obligation.cause.span,
kind: ErrKind::UnimplementedConstVal("could not resolve")
.into(),
kind: ErrKind::CouldNotResolve.into(),
})))
}
},
Expand Down
8 changes: 5 additions & 3 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2075,15 +2075,17 @@ impl<'a, 'gcx, 'tcx> AdtDef {
} else {
info!("invalid enum discriminant: {:#?}", val);
::middle::const_val::struct_error(
tcx,
tcx.def_span(expr_did),
tcx.at(tcx.def_span(expr_did)),
"constant evaluation of enum discriminant resulted in non-integer",
).emit();
None
}
}
Err(err) => {
err.report(tcx, tcx.def_span(expr_did), "enum discriminant");
err.report_as_error(
tcx.at(tcx.def_span(expr_did)),
"could not evaluate enum discriminant",
);
if !expr_did.is_local() {
span_bug!(tcx.def_span(expr_did),
"variant discriminant evaluation succeeded \
Expand Down
Loading

0 comments on commit 7d0d7ea

Please sign in to comment.