Skip to content

Commit

Permalink
Rollup merge of rust-lang#120699 - nnethercote:rm-useless-TRACK_DIAGN…
Browse files Browse the repository at this point in the history
…OSTIC-calls, r=oli-obk

Document `TRACK_DIAGNOSTIC` calls.

r? ````@cjgillot````
  • Loading branch information
matthiaskrgr authored Mar 14, 2024
2 parents 13b2c43 + 7ef605b commit 7acaa32
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 76 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_errors/src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2086,7 +2086,7 @@ impl HumanEmitter {
}
if !self.short_message {
for child in children {
assert!(child.level.can_be_top_or_sub().1);
assert!(child.level.can_be_subdiag());
let span = &child.span;
if let Err(err) = self.emit_messages_default_inner(
span,
Expand Down
162 changes: 92 additions & 70 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,12 +526,15 @@ pub enum StashKey {
UndeterminedMacroResolution,
}

fn default_track_diagnostic(diag: DiagInner, f: &mut dyn FnMut(DiagInner)) {
fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
(*f)(diag)
}

pub static TRACK_DIAGNOSTIC: AtomicRef<fn(DiagInner, &mut dyn FnMut(DiagInner))> =
AtomicRef::new(&(default_track_diagnostic as _));
/// Diagnostics emitted by `DiagCtxtInner::emit_diagnostic` are passed through this function. Used
/// for tracking by incremental, to replay diagnostics as necessary.
pub static TRACK_DIAGNOSTIC: AtomicRef<
fn(DiagInner, &mut dyn FnMut(DiagInner) -> Option<ErrorGuaranteed>) -> Option<ErrorGuaranteed>,
> = AtomicRef::new(&(default_track_diagnostic as _));

#[derive(Copy, Clone, Default)]
pub struct DiagCtxtFlags {
Expand Down Expand Up @@ -1422,74 +1425,103 @@ impl DiagCtxtInner {

// Return value is only `Some` if the level is `Error` or `DelayedBug`.
fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option<ErrorGuaranteed> {
assert!(diagnostic.level.can_be_top_or_sub().0);

if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
// The `LintExpectationId` can be stable or unstable depending on when it was created.
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
// a stable one by the `LintLevelsBuilder`.
if let LintExpectationId::Unstable { .. } = expectation_id {
self.unstable_expect_diagnostics.push(diagnostic);
return None;
}
self.suppressed_expected_diag = true;
self.fulfilled_expectations.insert(expectation_id.normalize());
}

if diagnostic.has_future_breakage() {
// Future breakages aren't emitted if they're `Level::Allow`,
// but they still need to be constructed and stashed below,
// so they'll trigger the must_produce_diag check.
self.suppressed_expected_diag = true;
assert!(matches!(diagnostic.level, Error | Warning | Allow));
self.future_breakage_diagnostics.push(diagnostic.clone());
}

// Note that because this comes before the `match` below,
// `-Zeagerly-emit-delayed-bugs` continues to work even after we've
// issued an error and stopped recording new delayed bugs.
if diagnostic.level == DelayedBug && self.flags.eagerly_emit_delayed_bugs {
diagnostic.level = Error;
}

// We call TRACK_DIAGNOSTIC with an empty closure for the cases that
// return early *and* have some kind of side-effect, except where
// noted.
match diagnostic.level {
// This must come after the possible promotion of `DelayedBug` to
// `Error` above.
Fatal | Error if self.treat_next_err_as_bug() => {
diagnostic.level = Bug;
Bug => {}
Fatal | Error => {
if self.treat_next_err_as_bug() {
// `Fatal` and `Error` can be promoted to `Bug`.
diagnostic.level = Bug;
}
}
DelayedBug => {
// If we have already emitted at least one error, we don't need
// to record the delayed bug, because it'll never be used.
return if let Some(guar) = self.has_errors() {
Some(guar)
// Note that because we check these conditions first,
// `-Zeagerly-emit-delayed-bugs` and `-Ztreat-err-as-bug`
// continue to work even after we've issued an error and
// stopped recording new delayed bugs.
if self.flags.eagerly_emit_delayed_bugs {
// `DelayedBug` can be promoted to `Error` or `Bug`.
if self.treat_next_err_as_bug() {
diagnostic.level = Bug;
} else {
diagnostic.level = Error;
}
} else {
let backtrace = std::backtrace::Backtrace::capture();
// This `unchecked_error_guaranteed` is valid. It is where the
// `ErrorGuaranteed` for delayed bugs originates. See
// `DiagCtxtInner::drop`.
#[allow(deprecated)]
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
self.delayed_bugs
.push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar));
Some(guar)
};
// If we have already emitted at least one error, we don't need
// to record the delayed bug, because it'll never be used.
return if let Some(guar) = self.has_errors() {
Some(guar)
} else {
// No `TRACK_DIAGNOSTIC` call is needed, because the
// incremental session is deleted if there is a delayed
// bug. This also saves us from cloning the diagnostic.
let backtrace = std::backtrace::Backtrace::capture();
// This `unchecked_error_guaranteed` is valid. It is where the
// `ErrorGuaranteed` for delayed bugs originates. See
// `DiagCtxtInner::drop`.
#[allow(deprecated)]
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
self.delayed_bugs
.push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar));
Some(guar)
};
}
}
ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect`
Warning => {
if !self.flags.can_emit_warnings {
// We are not emitting warnings.
if diagnostic.has_future_breakage() {
// The side-effect is at the top of this method.
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
}
return None;
}
}
Warning if !self.flags.can_emit_warnings => {
Note | Help | FailureNote => {}
OnceNote | OnceHelp => panic!("bad level: {:?}", diagnostic.level),
Allow => {
// Nothing emitted for allowed lints.
if diagnostic.has_future_breakage() {
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
// The side-effect is at the top of this method.
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
self.suppressed_expected_diag = true;
}
return None;
}
Allow | Expect(_) => {
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
return None;
Expect(expect_id) | ForceWarning(Some(expect_id)) => {
// Diagnostics created before the definition of `HirId`s are
// unstable and can not yet be stored. Instead, they are
// buffered until the `LintExpectationId` is replaced by a
// stable one by the `LintLevelsBuilder`.
if let LintExpectationId::Unstable { .. } = expect_id {
// We don't call TRACK_DIAGNOSTIC because we wait for the
// unstable ID to be updated, whereupon the diagnostic will
// be passed into this method again.
self.unstable_expect_diagnostics.push(diagnostic);
return None;
}
self.fulfilled_expectations.insert(expect_id.normalize());
if let Expect(_) = diagnostic.level {
// Nothing emitted here for expected lints.
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
self.suppressed_expected_diag = true;
return None;
}
}
_ => {}
}

let mut guaranteed = None;
(*TRACK_DIAGNOSTIC)(diagnostic, &mut |mut diagnostic| {
TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| {
if let Some(code) = diagnostic.code {
self.emitted_diagnostic_codes.insert(code);
}
Expand Down Expand Up @@ -1552,17 +1584,17 @@ impl DiagCtxtInner {
// `ErrorGuaranteed` for errors and lint errors originates.
#[allow(deprecated)]
let guar = ErrorGuaranteed::unchecked_error_guaranteed();
guaranteed = Some(guar);
if is_lint {
self.lint_err_guars.push(guar);
} else {
self.err_guars.push(guar);
}
self.panic_if_treat_err_as_bug();
Some(guar)
} else {
None
}
});

guaranteed
})
}

fn treat_err_as_bug(&self) -> bool {
Expand Down Expand Up @@ -1863,23 +1895,13 @@ impl Level {
matches!(*self, FailureNote)
}

pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
match self {
Expect(id) | ForceWarning(Some(id)) => Some(*id),
_ => None,
}
}

// Can this level be used in a top-level diagnostic message and/or a
// subdiagnostic message?
fn can_be_top_or_sub(&self) -> (bool, bool) {
// Can this level be used in a subdiagnostic message?
fn can_be_subdiag(&self) -> bool {
match self {
Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow
| Expect(_) => (true, false),

Warning | Note | Help => (true, true),
| Expect(_) => false,

OnceNote | OnceHelp => (false, true),
Warning | Note | Help | OnceNote | OnceHelp => true,
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_interface/src/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
/// This is a callback from `rustc_errors` as it cannot access the implicit state
/// in `rustc_middle` otherwise. It is used when diagnostic messages are
/// emitted and stores them in the current query, if there is one.
fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner)) {
fn track_diagnostic<R>(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
tls::with_context_opt(|icx| {
if let Some(icx) = icx {
if let Some(diagnostics) = icx.diagnostics {
Expand All @@ -38,11 +38,11 @@ fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner)) {

// Diagnostics are tracked, we can ignore the dependency.
let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() };
return tls::enter_context(&icx, move || (*f)(diagnostic));
tls::enter_context(&icx, move || (*f)(diagnostic))
} else {
// In any other case, invoke diagnostics anyway.
(*f)(diagnostic)
}

// In any other case, invoke diagnostics anyway.
(*f)(diagnostic);
})
}

Expand Down

0 comments on commit 7acaa32

Please sign in to comment.