Skip to content

Commit

Permalink
Auto merge of #71049 - eddyb:const-err, r=oli-obk
Browse files Browse the repository at this point in the history
Add `ConstKind::Error` and convert `ErrorHandled::Reported` to it.

By replicating the `ty::Error` approach to encoding "an error has occurred", all of the mechanisms that skip redundant/downstream errors are engaged and help out (see the reduction in test output).

This PR also adds `ErrorHandled::Linted` for the lint case because using `ErrorHandled::Reported` *without* having emitted an error that is *guaranteed* to stop compilation, is incorrect now.

r? @oli-obk cc @rust-lang/wg-const-eval @varkor @yodaldevoid
  • Loading branch information
bors committed Apr 17, 2020
2 parents b2c1a60 + 77f38dc commit 8d67f57
Show file tree
Hide file tree
Showing 57 changed files with 193 additions and 408 deletions.
7 changes: 5 additions & 2 deletions src/librustc_codegen_ssa/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::glue;
use crate::traits::*;
use crate::MemFlags;

use rustc_errors::ErrorReported;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{ConstValue, ErrorHandled, Pointer, Scalar};
use rustc_middle::ty::layout::TyAndLayout;
Expand Down Expand Up @@ -447,8 +448,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|err| {
match err {
// errored or at least linted
ErrorHandled::Reported => {}
ErrorHandled::TooGeneric => bug!("codgen encountered polymorphic constant"),
ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {}
ErrorHandled::TooGeneric => {
bug!("codegen encountered polymorphic constant")
}
}
// Allow RalfJ to sleep soundly knowing that even refactorings that remove
// the above error (or silence it under some conditions) will not cause UB.
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_infer/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@ pub fn unexpected_hidden_region_diagnostic(
// down this path which gives a decent human readable
// explanation.
//
// (*) if not, the `tainted_by_errors` flag would be set to
// true in any case, so we wouldn't be here at all.
// (*) if not, the `tainted_by_errors` field would be set to
// `Some(ErrorReported)` in any case, so we wouldn't be here at all.
note_and_explain_free_region(
tcx,
&mut err,
Expand Down
5 changes: 4 additions & 1 deletion src/librustc_infer/infer/freshen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
bug!("unexpected const {:?}", ct)
}

ty::ConstKind::Param(_) | ty::ConstKind::Value(_) | ty::ConstKind::Unevaluated(..) => {}
ty::ConstKind::Param(_)
| ty::ConstKind::Value(_)
| ty::ConstKind::Unevaluated(..)
| ty::ConstKind::Error => {}
}

ct.super_fold_with(self)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_infer/infer/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> {
match c.val {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
self.err = Some(FixupError::UnresolvedConst(vid));
return self.tcx().consts.err;
return self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty: c.ty });
}
ty::ConstKind::Infer(InferConst::Fresh(_)) => {
bug!("Unexpected const in full const resolver: {:?}", c);
Expand Down
57 changes: 25 additions & 32 deletions src/librustc_middle/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::ty::{self, layout, Ty};

use backtrace::Backtrace;
use rustc_data_structures::sync::Lock;
use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorReported};
use rustc_hir as hir;
use rustc_hir::definitions::DefPathData;
use rustc_macros::HashStable;
Expand All @@ -19,25 +19,16 @@ use std::{any::Any, fmt, mem};

#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)]
pub enum ErrorHandled {
/// Already reported a lint or an error for this evaluation.
Reported,
/// Already reported an error for this evaluation, and the compilation is
/// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
Reported(ErrorReported),
/// Already emitted a lint for this evaluation.
Linted,
/// Don't emit an error, the evaluation failed because the MIR was generic
/// and the substs didn't fully monomorphize it.
TooGeneric,
}

impl ErrorHandled {
pub fn assert_reported(self) {
match self {
ErrorHandled::Reported => {}
ErrorHandled::TooGeneric => bug!(
"MIR interpretation failed without reporting an error \
even though it was fully monomorphized"
),
}
}
}

CloneTypeFoldableImpls! {
ErrorHandled,
}
Expand Down Expand Up @@ -84,15 +75,12 @@ impl<'tcx> ConstEvalErr<'tcx> {
tcx: TyCtxtAt<'tcx>,
message: &str,
emit: impl FnOnce(DiagnosticBuilder<'_>),
) -> Result<(), ErrorHandled> {
) -> ErrorHandled {
self.struct_generic(tcx, message, emit, None)
}

pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
match self.struct_error(tcx, message, |mut e| e.emit()) {
Ok(_) => ErrorHandled::Reported,
Err(x) => x,
}
self.struct_error(tcx, message, |mut e| e.emit())
}

pub fn report_as_lint(
Expand All @@ -102,7 +90,7 @@ impl<'tcx> ConstEvalErr<'tcx> {
lint_root: hir::HirId,
span: Option<Span>,
) -> ErrorHandled {
match self.struct_generic(
self.struct_generic(
tcx,
message,
|mut lint: DiagnosticBuilder<'_>| {
Expand All @@ -122,10 +110,7 @@ impl<'tcx> ConstEvalErr<'tcx> {
lint.emit();
},
Some(lint_root),
) {
Ok(_) => ErrorHandled::Reported,
Err(err) => err,
}
)
}

/// Create a diagnostic for this const eval error.
Expand All @@ -143,12 +128,14 @@ impl<'tcx> ConstEvalErr<'tcx> {
message: &str,
emit: impl FnOnce(DiagnosticBuilder<'_>),
lint_root: Option<hir::HirId>,
) -> Result<(), ErrorHandled> {
) -> ErrorHandled {
let must_error = match self.error {
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
return Err(ErrorHandled::TooGeneric);
return ErrorHandled::TooGeneric;
}
err_inval!(TypeckError(error_reported)) => {
return ErrorHandled::Reported(error_reported);
}
err_inval!(TypeckError) => return Err(ErrorHandled::Reported),
// We must *always* hard error on these, even if the caller wants just a lint.
err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
_ => false,
Expand Down Expand Up @@ -183,6 +170,7 @@ impl<'tcx> ConstEvalErr<'tcx> {
// caller thinks anyway.
// See <https://github.com/rust-lang/rust/pull/63152>.
finish(struct_error(tcx, &err_msg), None);
ErrorHandled::Reported(ErrorReported)
} else {
// Regular case.
if let Some(lint_root) = lint_root {
Expand All @@ -200,12 +188,13 @@ impl<'tcx> ConstEvalErr<'tcx> {
tcx.span,
|lint| finish(lint.build(message), Some(err_msg)),
);
ErrorHandled::Linted
} else {
// Report as hard error.
finish(struct_error(tcx, message), Some(err_msg));
ErrorHandled::Reported(ErrorReported)
}
}
Ok(())
}
}

Expand Down Expand Up @@ -246,7 +235,9 @@ fn print_backtrace(backtrace: &mut Backtrace) {
impl From<ErrorHandled> for InterpErrorInfo<'_> {
fn from(err: ErrorHandled) -> Self {
match err {
ErrorHandled::Reported => err_inval!(ReferencedConstant),
ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
err_inval!(ReferencedConstant)
}
ErrorHandled::TooGeneric => err_inval!(TooGeneric),
}
.into()
Expand Down Expand Up @@ -288,7 +279,7 @@ pub enum InvalidProgramInfo<'tcx> {
/// which already produced an error.
ReferencedConstant,
/// Abort in case type errors are reached.
TypeckError,
TypeckError(ErrorReported),
/// An error occurred during layout computation.
Layout(layout::LayoutError<'tcx>),
/// An invalid transmute happened.
Expand All @@ -301,7 +292,9 @@ impl fmt::Debug for InvalidProgramInfo<'_> {
match self {
TooGeneric => write!(f, "encountered overly generic constant"),
ReferencedConstant => write!(f, "referenced constant has errors"),
TypeckError => write!(f, "encountered constants with type errors, stopping evaluation"),
TypeckError(ErrorReported) => {
write!(f, "encountered constants with type errors, stopping evaluation")
}
Layout(ref err) => write!(f, "{}", err),
TransmuteSizeDiff(from_ty, to_ty) => write!(
f,
Expand Down
12 changes: 6 additions & 6 deletions src/librustc_middle/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub struct CommonLifetimes<'tcx> {
}

pub struct CommonConsts<'tcx> {
pub err: &'tcx Const<'tcx>,
pub unit: &'tcx Const<'tcx>,
}

pub struct LocalTableInContext<'a, V> {
Expand Down Expand Up @@ -410,8 +410,8 @@ pub struct TypeckTables<'tcx> {
pub used_trait_imports: Lrc<DefIdSet>,

/// If any errors occurred while type-checking this body,
/// this field will be set to `true`.
pub tainted_by_errors: bool,
/// this field will be set to `Some(ErrorReported)`.
pub tainted_by_errors: Option<ErrorReported>,

/// All the opaque types that are restricted to concrete types
/// by this function.
Expand Down Expand Up @@ -447,7 +447,7 @@ impl<'tcx> TypeckTables<'tcx> {
fru_field_types: Default::default(),
coercion_casts: Default::default(),
used_trait_imports: Lrc::new(Default::default()),
tainted_by_errors: false,
tainted_by_errors: None,
concrete_opaque_types: Default::default(),
upvar_list: Default::default(),
generator_interior_types: Default::default(),
Expand Down Expand Up @@ -858,9 +858,9 @@ impl<'tcx> CommonConsts<'tcx> {
let mk_const = |c| interners.const_.intern(c, |c| Interned(interners.arena.alloc(c))).0;

CommonConsts {
err: mk_const(ty::Const {
unit: mk_const(ty::Const {
val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::zst())),
ty: types.err,
ty: types.unit,
}),
}
}
Expand Down
10 changes: 2 additions & 8 deletions src/librustc_middle/ty/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,7 @@ impl FlagComputation {
| &ty::Str
| &ty::Foreign(..) => {}

// You might think that we could just return Error for
// any type containing Error as a component, and get
// rid of the TypeFlags::HAS_TY_ERR flag -- likewise for ty_bot (with
// the exception of function types that return bot).
// But doing so caused sporadic memory corruption, and
// neither I (tjc) nor nmatsakis could figure out why,
// so we're doing it this way.
&ty::Error => self.add_flags(TypeFlags::HAS_TY_ERR),
&ty::Error => self.add_flags(TypeFlags::HAS_ERROR),

&ty::Param(_) => {
self.add_flags(TypeFlags::HAS_TY_PARAM);
Expand Down Expand Up @@ -239,6 +232,7 @@ impl FlagComputation {
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
}
ty::ConstKind::Value(_) => {}
ty::ConstKind::Error => self.add_flags(TypeFlags::HAS_ERROR),
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_middle/ty/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
}
fn references_error(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_TY_ERR)
self.has_type_flags(TypeFlags::HAS_ERROR)
}
fn has_param_types_or_consts(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM)
Expand Down
7 changes: 4 additions & 3 deletions src/librustc_middle/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sorted_map::SortedIndexMultiMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{self, par_iter, ParallelIterator};
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX};
Expand Down Expand Up @@ -567,8 +568,8 @@ bitflags! {
| TypeFlags::HAS_TY_OPAQUE.bits
| TypeFlags::HAS_CT_PROJECTION.bits;

/// Is an error type reachable?
const HAS_TY_ERR = 1 << 13;
/// Is an error type/const reachable?
const HAS_ERROR = 1 << 13;

/// Does this have any region that "appears free" in the type?
/// Basically anything but [ReLateBound] and [ReErased].
Expand Down Expand Up @@ -2388,7 +2389,7 @@ impl<'tcx> AdtDef {
None
}
}
Err(ErrorHandled::Reported) => {
Err(ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted) => {
if !expr_did.is_local() {
span_bug!(
tcx.def_span(expr_did),
Expand Down
1 change: 1 addition & 0 deletions src/librustc_middle/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,7 @@ pub trait PrettyPrinter<'tcx>:
self.pretty_print_bound_var(debruijn, bound_var)?
}
ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
ty::ConstKind::Error => p!(write("[const error]")),
};
Ok(self)
}
Expand Down
12 changes: 12 additions & 0 deletions src/librustc_middle/ty/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,12 +510,21 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
let tcx = relation.tcx();

let eagerly_eval = |x: &'tcx ty::Const<'tcx>| {
// FIXME(eddyb) this doesn't account for lifetime inference variables
// being erased by `eval`, *nor* for the polymorphic aspect of `eval`.
// That is, we could always use `eval` and it will just return the
// old value back if it doesn't succeed.
if !x.val.needs_infer() {
return x.eval(tcx, relation.param_env()).val;
}
x.val
};

// FIXME(eddyb) doesn't look like everything below checks that `a.ty == b.ty`.
// We could probably always assert it early, as `const` generic parameters
// are not allowed to depend on other generic parameters, i.e. are concrete.
// (although there could be normalization differences)

// Currently, the values that can be unified are primitive types,
// and those that derive both `PartialEq` and `Eq`, corresponding
// to structural-match types.
Expand All @@ -524,6 +533,9 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
// The caller should handle these cases!
bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b)
}

(ty::ConstKind::Error, _) | (_, ty::ConstKind::Error) => Ok(ty::ConstKind::Error),

(ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => {
return Ok(a);
}
Expand Down
14 changes: 8 additions & 6 deletions src/librustc_middle/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,9 +1022,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> {
ty::ConstKind::Unevaluated(did, substs, promoted) => {
ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted)
}
ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(..) => {
*self
}
ty::ConstKind::Value(_)
| ty::ConstKind::Bound(..)
| ty::ConstKind::Placeholder(..)
| ty::ConstKind::Error => *self,
}
}

Expand All @@ -1033,9 +1034,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> {
ty::ConstKind::Infer(ic) => ic.visit_with(visitor),
ty::ConstKind::Param(p) => p.visit_with(visitor),
ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor),
ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
false
}
ty::ConstKind::Value(_)
| ty::ConstKind::Bound(..)
| ty::ConstKind::Placeholder(_)
| ty::ConstKind::Error => false,
}
}
}
Expand Down
Loading

0 comments on commit 8d67f57

Please sign in to comment.