clean up some const error reporting around promoteds

This commit is contained in:
Ralf Jung 2021-01-24 12:50:30 +01:00
parent 4d0dd02ee0
commit 48f9dbfd59
18 changed files with 66 additions and 144 deletions

View file

@ -298,6 +298,8 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
tcx.def_span(def.did),
key.param_env,
CompileTimeInterpreter::new(tcx.sess.const_eval_limit()),
// Statics (and promoteds inside statics) may access other statics, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
MemoryExtra { can_access_statics: is_static },
);
@ -305,83 +307,35 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
Err(error) => {
let err = ConstEvalErr::new(&ecx, error, None);
// errors in statics are always emitted as fatal errors
if is_static {
// Ensure that if the above error was either `TooGeneric` or `Reported`
// an error must be reported.
let v = err.report_as_error(
ecx.tcx.at(ecx.cur_span()),
"could not evaluate static initializer",
);
// If this is `Reveal:All`, then we need to make sure an error is reported but if
// this is `Reveal::UserFacing`, then it's expected that we could get a
// `TooGeneric` error. When we fall back to `Reveal::All`, then it will either
// succeed or we'll report this error then.
if key.param_env.reveal() == Reveal::All {
tcx.sess.delay_span_bug(
err.span,
&format!("static eval failure did not emit an error: {:#?}", v),
);
}
Err(v)
} else if let Some(def) = def.as_local() {
// constant defined in this crate, we can figure out a lint level!
match tcx.def_kind(def.did.to_def_id()) {
// constants never produce a hard error at the definition site. Anything else is
// a backwards compatibility hazard (and will break old versions of winapi for
// sure)
//
// note that validation may still cause a hard error on this very same constant,
// because any code that existed before validation could not have failed
// validation thus preventing such a hard error from being a backwards
// compatibility hazard
DefKind::Const | DefKind::AssocConst => {
let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
Err(err.report_as_lint(
tcx.at(tcx.def_span(def.did)),
"any use of this value will cause an error",
hir_id,
Some(err.span),
))
}
// promoting runtime code is only allowed to error if it references broken
// constants any other kind of error will be reported to the user as a
// deny-by-default lint
_ => {
if let Some(p) = cid.promoted {
let span = tcx.promoted_mir_opt_const_arg(def.to_global())[p].span;
if let err_inval!(ReferencedConstant) = err.error {
Err(err.report_as_error(
tcx.at(span),
"evaluation of constant expression failed",
))
} else {
Err(err.report_as_lint(
tcx.at(span),
"reaching this expression at runtime will panic or abort",
tcx.hir().local_def_id_to_hir_id(def.did),
Some(err.span),
))
}
// anything else (array lengths, enum initializers, constant patterns) are
// reported as hard errors
} else {
Err(err.report_as_error(
ecx.tcx.at(ecx.cur_span()),
"evaluation of constant value failed",
))
}
}
}
// Some CTFE errors raise just a lint, not a hard error; see
// <https://github.com/rust-lang/rust/issues/71800>.
let emit_as_lint = if let Some(def) = def.as_local() {
// (Associated) consts only emit a lint, since they might be unused.
matches!(tcx.def_kind(def.did.to_def_id()), DefKind::Const | DefKind::AssocConst)
} else {
// use of broken constant from other crate
Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant"))
// use of broken constant from other crate: always an error
false
};
if emit_as_lint {
let hir_id = tcx.hir().local_def_id_to_hir_id(def.as_local().unwrap().did);
Err(err.report_as_lint(
tcx.at(tcx.def_span(def.did)),
"any use of this value will cause an error",
hir_id,
Some(err.span),
))
} else {
let msg = if is_static {
"could not evaluate static initializer"
} else {
"evaluation of constant value failed"
};
Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), msg))
}
}
Ok(mplace) => {
// Since evaluation had no errors, valiate the resulting constant:
// Since evaluation had no errors, validate the resulting constant.
// This is a separate `try` block to provide more targeted error reporting.
let validation = try {
let mut ref_tracking = RefTracking::new(mplace);
let mut inner = false;
@ -399,7 +353,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
}
};
if let Err(error) = validation {
// Validation failed, report an error
// Validation failed, report an error. This is always a hard error.
let err = ConstEvalErr::new(&ecx, error, None);
Err(err.struct_error(
ecx.tcx,