Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix EmissionGuarantee #119097

Merged
merged 10 commits into from
Dec 22, 2023
64 changes: 28 additions & 36 deletions compiler/rustc_errors/src/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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) => {
Expand Down Expand Up @@ -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) => {
Expand All @@ -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 BugAbort {
type EmitResult = !;

impl EmissionGuarantee for Bug {
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) => {
Expand All @@ -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) => {
Expand All @@ -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) => {
Expand Down Expand Up @@ -313,16 +305,16 @@ 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,
/// in which case the emission will be delayed as a bug.
///
/// 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();
}
Expand Down Expand Up @@ -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()
}
Expand Down
36 changes: 18 additions & 18 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
}

Expand Down Expand Up @@ -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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yay

}

#[inline]
Expand Down Expand Up @@ -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)
}

Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_parse/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use rustc_ast::ast::{self, AttrStyle};
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::util::unicode::contains_text_flow_control_chars;
use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
use rustc_errors::{
error_code, Applicability, Diagnostic, DiagnosticBuilder, FatalAbort, StashKey,
};
use rustc_lexer::unescape::{self, EscapeError, Mode};
use rustc_lexer::{Base, DocStyle, RawStrError};
use rustc_lexer::{Cursor, LiteralKind};
Expand Down Expand Up @@ -344,7 +346,7 @@ impl<'a> StringReader<'a> {
to_pos: BytePos,
m: &str,
c: char,
) -> DiagnosticBuilder<'a, !> {
) -> DiagnosticBuilder<'a, FatalAbort> {
self.sess
.dcx
.struct_span_fatal(self.mk_sp(from_pos, to_pos), format!("{}: {}", m, escaped_char(c)))
Expand Down
19 changes: 11 additions & 8 deletions compiler/rustc_session/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc};
use rustc_errors::{emitter::SilentEmitter, DiagCtxt};
use rustc_errors::{
fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
ErrorGuaranteed, IntoDiagnostic, Level, MultiSpan, Noted, StashKey,
ErrorGuaranteed, FatalAbort, IntoDiagnostic, Level, MultiSpan, StashKey,
};
use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
use rustc_span::edition::Edition;
Expand Down Expand Up @@ -346,26 +346,26 @@ impl ParseSess {
#[track_caller]
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.dcx, Level::Note)
}

#[track_caller]
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()
}

#[track_caller]
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.dcx, Level::Fatal)
}

#[track_caller]
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()
}

Expand All @@ -386,7 +386,10 @@ impl ParseSess {

#[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> {
self.dcx.struct_fatal(msg)
}
}
27 changes: 15 additions & 12 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use rustc_errors::json::JsonEmitter;
use rustc_errors::registry::Registry;
use rustc_errors::{
error_code, fallback_fluent_bundle, DiagCtxt, DiagnosticBuilder, DiagnosticId,
DiagnosticMessage, ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle,
MultiSpan, Noted, TerminalUrl,
DiagnosticMessage, ErrorGuaranteed, FatalAbort, FluentBundle, IntoDiagnostic,
LazyFallbackBundle, MultiSpan, TerminalUrl,
};
use rustc_macros::HashStable_Generic;
pub use rustc_span::def_id::StableCrateId;
Expand Down Expand Up @@ -428,7 +428,7 @@ impl Session {
&self,
sp: S,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, !> {
) -> DiagnosticBuilder<'_, FatalAbort> {
self.dcx().struct_span_fatal(sp, msg)
}
#[rustc_lint_diagnostics]
Expand All @@ -437,11 +437,14 @@ impl Session {
sp: S,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, !> {
) -> DiagnosticBuilder<'_, FatalAbort> {
self.dcx().struct_span_fatal_with_code(sp, msg, code)
}
#[rustc_lint_diagnostics]
pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
pub fn struct_fatal(
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, FatalAbort> {
self.dcx().struct_fatal(msg)
}

Expand Down Expand Up @@ -525,23 +528,23 @@ impl Session {
#[track_caller]
pub fn create_note<'a>(
&'a self,
note: impl IntoDiagnostic<'a, Noted>,
) -> DiagnosticBuilder<'a, Noted> {
note: impl IntoDiagnostic<'a, ()>,
) -> DiagnosticBuilder<'a, ()> {
self.parse_sess.create_note(note)
}
#[track_caller]
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.parse_sess.emit_note(note)
}
#[track_caller]
pub fn create_fatal<'a>(
&'a self,
fatal: impl IntoDiagnostic<'a, !>,
) -> DiagnosticBuilder<'a, !> {
fatal: impl IntoDiagnostic<'a, FatalAbort>,
) -> DiagnosticBuilder<'a, FatalAbort> {
self.parse_sess.create_fatal(fatal)
}
#[track_caller]
pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, !>) -> ! {
pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! {
self.parse_sess.emit_fatal(fatal)
}
#[inline]
Expand Down Expand Up @@ -1780,7 +1783,7 @@ impl EarlyDiagCtxt {
pub fn early_struct_error(
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, !> {
) -> DiagnosticBuilder<'_, FatalAbort> {
self.dcx.struct_fatal(msg)
}

Expand Down