1
Fork 0

Add EmitResult associated type to EmissionGuarantee.

This lets different error levels share the same return type from
`emit_*`.

- A lot of inconsistencies in the `DiagCtxt` API are removed.
- `Noted` is removed.
- `FatalAbort` is introduced for fatal errors (abort via `raise`),
  replacing the `EmissionGuarantee` impl for `!`.
- `Bug` is renamed `BugAbort` (to avoid clashing with `Level::Bug` and
  to mirror `FatalAbort`), and modified to work in the new way with bug
  errors (abort via panic).
- Various diagnostic creators and emitters updated to the new, better
  signatures. Note that `DiagCtxt::bug` no longer needs to call
  `panic_any`, because `emit` handles that.

Also shorten the obnoxiously long
`diagnostic_builder_emit_producing_guarantee` name.
This commit is contained in:
Nicholas Nethercote 2023-12-18 16:31:15 +11:00
parent 3a5f28f7e8
commit f5459201e0
5 changed files with 76 additions and 76 deletions

View file

@ -101,11 +101,15 @@ rustc_data_structures::static_assert_size!(
/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
/// (or "proof") token that the emission happened.
pub trait EmissionGuarantee: Sized {
/// This exists so that bugs and fatal errors can both result in `!` (an
/// abort) when emitted, but have different aborting behaviour.
type EmitResult = Self;
/// Implementation of `DiagnosticBuilder::emit`, fully controlled by each
/// `impl` of `EmissionGuarantee`, to make it impossible to create a value
/// of `Self` without actually performing the emission.
/// of `Self::EmitResult` without actually performing the emission.
#[track_caller]
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self;
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
}
impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
@ -119,7 +123,7 @@ impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
// FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
impl EmissionGuarantee for ErrorGuaranteed {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
match db.inner.state {
// First `.emit()` call, the `&DiagCtxt` is still available.
DiagnosticBuilderState::Emittable(dcx) => {
@ -160,7 +164,7 @@ impl EmissionGuarantee for ErrorGuaranteed {
// FIXME(eddyb) should there be a `Option<ErrorGuaranteed>` impl as well?
impl EmissionGuarantee for () {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
match db.inner.state {
// First `.emit()` call, the `&DiagCtxt` is still available.
DiagnosticBuilderState::Emittable(dcx) => {
@ -174,34 +178,15 @@ impl EmissionGuarantee for () {
}
}
/// Marker type which enables implementation of `create_note` and `emit_note` functions for
/// note-without-error struct diagnostics.
#[derive(Copy, Clone)]
pub struct Noted;
impl EmissionGuarantee for Noted {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
match db.inner.state {
// First `.emit()` call, the `&DiagCtxt` is still available.
DiagnosticBuilderState::Emittable(dcx) => {
db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
}
// `.emit()` was previously called, disallowed from repeating it.
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
}
Noted
}
}
/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for
/// bug struct diagnostics.
/// bug diagnostics.
#[derive(Copy, Clone)]
pub struct Bug;
pub struct BugAbort;
impl EmissionGuarantee for Bug {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
impl EmissionGuarantee for BugAbort {
type EmitResult = !;
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
match db.inner.state {
// First `.emit()` call, the `&DiagCtxt` is still available.
DiagnosticBuilderState::Emittable(dcx) => {
@ -217,8 +202,15 @@ impl EmissionGuarantee for Bug {
}
}
impl EmissionGuarantee for ! {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for
/// fatal diagnostics.
#[derive(Copy, Clone)]
pub struct FatalAbort;
impl EmissionGuarantee for FatalAbort {
type EmitResult = !;
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
match db.inner.state {
// First `.emit()` call, the `&DiagCtxt` is still available.
DiagnosticBuilderState::Emittable(dcx) => {
@ -235,7 +227,7 @@ impl EmissionGuarantee for ! {
}
impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
match db.inner.state {
// First `.emit()` call, the `&DiagCtxt` is still available.
DiagnosticBuilderState::Emittable(dcx) => {
@ -313,8 +305,8 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
/// but there are various places that rely on continuing to use `self`
/// after calling `emit`.
#[track_caller]
pub fn emit(&mut self) -> G {
G::diagnostic_builder_emit_producing_guarantee(self)
pub fn emit(&mut self) -> G::EmitResult {
G::emit_producing_guarantee(self)
}
/// Emit the diagnostic unless `delay` is true,
@ -322,7 +314,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
///
/// See `emit` and `delay_as_bug` for details.
#[track_caller]
pub fn emit_unless(&mut self, delay: bool) -> G {
pub fn emit_unless(&mut self, delay: bool) -> G::EmitResult {
if delay {
self.downgrade_to_delayed_bug();
}
@ -409,7 +401,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
/// In the meantime, though, callsites are required to deal with the "bug"
/// locally in whichever way makes the most sense.
#[track_caller]
pub fn delay_as_bug(&mut self) -> G {
pub fn delay_as_bug(&mut self) -> G::EmitResult {
self.downgrade_to_delayed_bug();
self.emit()
}

View file

@ -6,6 +6,7 @@
#![doc(rust_logo)]
#![feature(rustdoc_internals)]
#![feature(array_windows)]
#![feature(associated_type_defaults)]
#![feature(extract_if)]
#![feature(if_let_guard)]
#![feature(let_chains)]
@ -401,7 +402,7 @@ pub use diagnostic::{
AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
};
pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
pub use diagnostic_builder::{BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort};
pub use diagnostic_impls::{
DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, LabelKind,
@ -907,7 +908,7 @@ impl DiagCtxt {
&self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, !> {
) -> DiagnosticBuilder<'_, FatalAbort> {
let mut result = self.struct_fatal(msg);
result.set_span(span);
result
@ -921,7 +922,7 @@ impl DiagCtxt {
span: impl Into<MultiSpan>,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, !> {
) -> DiagnosticBuilder<'_, FatalAbort> {
let mut result = self.struct_span_fatal(span, msg);
result.code(code);
result
@ -930,7 +931,10 @@ impl DiagCtxt {
/// Construct a builder at the `Fatal` level with the `msg`.
#[rustc_lint_diagnostics]
#[track_caller]
pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
pub fn struct_fatal(
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, FatalAbort> {
DiagnosticBuilder::new(self, Level::Fatal, msg)
}
@ -1101,8 +1105,7 @@ impl DiagCtxt {
}
pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! {
DiagnosticBuilder::<diagnostic_builder::Bug>::new(self, Bug, msg).emit();
panic::panic_any(ExplicitBug);
DiagnosticBuilder::<BugAbort>::new(self, Bug, msg).emit()
}
#[inline]
@ -1294,37 +1297,34 @@ impl DiagCtxt {
pub fn create_fatal<'a>(
&'a self,
fatal: impl IntoDiagnostic<'a, !>,
) -> DiagnosticBuilder<'a, !> {
fatal: impl IntoDiagnostic<'a, FatalAbort>,
) -> DiagnosticBuilder<'a, FatalAbort> {
fatal.into_diagnostic(self, Level::Fatal)
}
pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, !>) -> ! {
pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! {
self.create_fatal(fatal).emit()
}
pub fn create_bug<'a>(
&'a self,
bug: impl IntoDiagnostic<'a, diagnostic_builder::Bug>,
) -> DiagnosticBuilder<'a, diagnostic_builder::Bug> {
bug: impl IntoDiagnostic<'a, BugAbort>,
) -> DiagnosticBuilder<'a, BugAbort> {
bug.into_diagnostic(self, Level::Bug)
}
pub fn emit_bug<'a>(
&'a self,
bug: impl IntoDiagnostic<'a, diagnostic_builder::Bug>,
) -> diagnostic_builder::Bug {
pub fn emit_bug<'a>(&'a self, bug: impl IntoDiagnostic<'a, BugAbort>) -> ! {
self.create_bug(bug).emit()
}
pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted {
pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, ()>) {
self.create_note(note).emit()
}
pub fn create_note<'a>(
&'a self,
note: impl IntoDiagnostic<'a, Noted>,
) -> DiagnosticBuilder<'a, Noted> {
note: impl IntoDiagnostic<'a, ()>,
) -> DiagnosticBuilder<'a, ()> {
note.into_diagnostic(self, Level::Note)
}