use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_middle::mir;
use rustc_session::config::nightly_options;
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
Forbidden,
}
+#[derive(Clone, Copy)]
+pub enum DiagnosticImportance {
+ /// An operation that must be removed for const-checking to pass.
+ Primary,
+
+ /// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere.
+ Secondary,
+}
+
/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
const STOPS_CONST_CHECKING: bool = false;
Status::Forbidden
}
+ fn importance(&self) -> DiagnosticImportance {
+ DiagnosticImportance::Primary
+ }
+
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = struct_span_err!(
ccx.tcx.sess,
Status::Unstable(sym::const_mut_refs)
}
+ fn importance(&self) -> DiagnosticImportance {
+ // Usually a side-effect of a `MutBorrow` somewhere.
+ DiagnosticImportance::Secondary
+ }
+
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
use super::*;
#[derive(Debug)]
- pub struct MutRef;
+ pub struct MutRef(pub mir::LocalKind);
impl NonConstOp for MutRef {
fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
+ fn importance(&self) -> DiagnosticImportance {
+ match self.0 {
+ mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
+ mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
+ DiagnosticImportance::Primary
+ }
+ }
+ }
+
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
}
#[derive(Debug)]
- pub struct FnPtr;
+ pub struct FnPtr(pub mir::LocalKind);
impl NonConstOp for FnPtr {
const STOPS_CONST_CHECKING: bool = true;
+ fn importance(&self) -> DiagnosticImportance {
+ match self.0 {
+ mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
+ mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
+ DiagnosticImportance::Primary
+ }
+ }
+ }
+
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, HirId, LangItem};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::{self, TraitEngine};
+use std::mem;
use std::ops::Deref;
use super::ops::{self, NonConstOp, Status};
span: Span,
const_checking_stopped: bool,
+
+ error_emitted: bool,
+ secondary_errors: Vec<Diagnostic>,
}
impl Deref for Validator<'mir, 'tcx> {
ccx,
qualifs: Default::default(),
const_checking_stopped: false,
+ error_emitted: false,
+ secondary_errors: Vec::new(),
}
}
self.check_item_predicates();
- for local in &body.local_decls {
+ for (idx, local) in body.local_decls.iter_enumerated() {
if local.internal {
continue;
}
self.span = local.source_info.span;
- self.check_local_or_return_ty(local.ty);
+ self.check_local_or_return_ty(local.ty, idx);
}
// impl trait is gone in MIR, so check the return type of a const fn by its signature
// instead of the type of the return place.
self.span = body.local_decls[RETURN_PLACE].source_info.span;
let return_ty = tcx.fn_sig(def_id).output();
- self.check_local_or_return_ty(return_ty.skip_binder());
+ self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE);
}
self.visit_body(&body);
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
check_return_ty_is_sync(tcx, &body, hir_id);
}
+
+ // If we got through const-checking without emitting any "primary" errors, emit any
+ // "secondary" errors if they occurred.
+ let secondary_errors = mem::take(&mut self.secondary_errors);
+ if !self.error_emitted {
+ for error in secondary_errors {
+ self.tcx.sess.diagnostic().emit_diagnostic(&error);
+ }
+ } else {
+ assert!(self.tcx.sess.has_errors());
+ }
}
pub fn qualifs_in_return_place(&mut self) -> ConstQualifs {
let mut err = op.build_error(self.ccx, span);
assert!(err.is_error());
- err.emit();
+
+ match op.importance() {
+ ops::DiagnosticImportance::Primary => {
+ self.error_emitted = true;
+ err.emit();
+ }
+
+ ops::DiagnosticImportance::Secondary => err.buffer(&mut self.secondary_errors),
+ }
if O::STOPS_CONST_CHECKING {
self.const_checking_stopped = true;
self.check_op_spanned(ops::StaticAccess, span)
}
- fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>) {
+ fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>, local: Local) {
+ let kind = self.body.local_kind(local);
+
for ty in ty.walk() {
let ty = match ty.unpack() {
GenericArgKind::Type(ty) => ty,
};
match *ty.kind() {
- ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef),
+ ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef(kind)),
ty::Opaque(..) => self.check_op(ops::ty::ImplTrait),
- ty::FnPtr(..) => self.check_op(ops::ty::FnPtr),
+ ty::FnPtr(..) => self.check_op(ops::ty::FnPtr(kind)),
ty::Dynamic(preds, _) => {
for pred in preds.iter() {