1
Fork 0

Auto merge of #84762 - cjgillot:resolve-span-opt, r=petrochenkov

Encode spans relative to the enclosing item -- enable on nightly

Follow-up to #84373 with the flag `-Zincremental-relative-spans` set by default.

This PR seeks to remove one of the main shortcomings of incremental: the handling of spans.
Changing the contents of a function may require redoing part of the compilation process for another function in another file because of span information is changed.
Within one file: all the spans in HIR change, so typechecking had to be re-done.
Between files: spans of associated types/consts/functions change, so type-based resolution needs to be re-done (hygiene information is stored in the span).

The flag `-Zincremental-relative-spans` encodes local spans relative to the span of an item, stored inside the `source_span` query.

Trap: stashed diagnostics are referenced by the "raw" span, so stealing them requires to remove the span's parent.

In order to avoid too much traffic in the span interner, span encoding uses the `ctxt_or_tag` field to encode:
- the parent when the `SyntaxContext` is 0;
- the `SyntaxContext` when the parent is `None`.
Even with this, the PR creates a lot of traffic to the Span interner, when a Span has both a LocalDefId parent and a non-root SyntaxContext. They appear in lowering, when we add a parent to all spans, including those which come from macros, and during inlining when we mark inlined spans.

The last commit changes how queries of `LocalDefId` manage their cache. I can put this in a separate PR if required.

Possible future directions:
- validate that all spans are marked in HIR validation;
- mark macro-expanded spans relative to the def-site and not the use-site.
This commit is contained in:
bors 2023-01-02 13:10:16 +00:00
commit fb9dfa8cef
43 changed files with 169 additions and 204 deletions

View file

@ -469,10 +469,12 @@ pub enum StashKey {
CallAssocMethod,
}
fn default_track_diagnostic(_: &Diagnostic) {}
fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) {
(*f)(d)
}
pub static TRACK_DIAGNOSTICS: AtomicRef<fn(&Diagnostic)> =
AtomicRef::new(&(default_track_diagnostic as fn(&_)));
pub static TRACK_DIAGNOSTICS: AtomicRef<fn(&mut Diagnostic, &mut dyn FnMut(&mut Diagnostic))> =
AtomicRef::new(&(default_track_diagnostic as _));
#[derive(Copy, Clone, Default)]
pub struct HandlerFlags {
@ -654,17 +656,19 @@ impl Handler {
/// Retrieve a stashed diagnostic with `steal_diagnostic`.
pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) {
let mut inner = self.inner.borrow_mut();
inner.stash((span, key), diag);
inner.stash((span.with_parent(None), key), diag);
}
/// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key.
pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> {
let mut inner = self.inner.borrow_mut();
inner.steal((span, key)).map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
inner
.steal((span.with_parent(None), key))
.map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
}
pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool {
self.inner.borrow().stashed_diagnostics.get(&(span, key)).is_some()
self.inner.borrow().stashed_diagnostics.get(&(span.with_parent(None), key)).is_some()
}
/// Emit all stashed diagnostics.
@ -1293,67 +1297,69 @@ impl HandlerInner {
&& !diagnostic.is_force_warn()
{
if diagnostic.has_future_breakage() {
(*TRACK_DIAGNOSTICS)(diagnostic);
(*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {});
}
return None;
}
(*TRACK_DIAGNOSTICS)(diagnostic);
if matches!(diagnostic.level, Level::Expect(_) | Level::Allow) {
(*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {});
return None;
}
if let Some(ref code) = diagnostic.code {
self.emitted_diagnostic_codes.insert(code.clone());
}
let mut guaranteed = None;
(*TRACK_DIAGNOSTICS)(diagnostic, &mut |diagnostic| {
if let Some(ref code) = diagnostic.code {
self.emitted_diagnostic_codes.insert(code.clone());
}
let already_emitted = |this: &mut Self| {
let mut hasher = StableHasher::new();
diagnostic.hash(&mut hasher);
let diagnostic_hash = hasher.finish();
!this.emitted_diagnostics.insert(diagnostic_hash)
};
// Only emit the diagnostic if we've been asked to deduplicate or
// haven't already emitted an equivalent diagnostic.
if !(self.flags.deduplicate_diagnostics && already_emitted(self)) {
debug!(?diagnostic);
debug!(?self.emitted_diagnostics);
let already_emitted_sub = |sub: &mut SubDiagnostic| {
debug!(?sub);
if sub.level != Level::OnceNote {
return false;
}
let already_emitted = |this: &mut Self| {
let mut hasher = StableHasher::new();
sub.hash(&mut hasher);
diagnostic.hash(&mut hasher);
let diagnostic_hash = hasher.finish();
debug!(?diagnostic_hash);
!self.emitted_diagnostics.insert(diagnostic_hash)
!this.emitted_diagnostics.insert(diagnostic_hash)
};
diagnostic.children.drain_filter(already_emitted_sub).for_each(|_| {});
// Only emit the diagnostic if we've been asked to deduplicate or
// haven't already emitted an equivalent diagnostic.
if !(self.flags.deduplicate_diagnostics && already_emitted(self)) {
debug!(?diagnostic);
debug!(?self.emitted_diagnostics);
let already_emitted_sub = |sub: &mut SubDiagnostic| {
debug!(?sub);
if sub.level != Level::OnceNote {
return false;
}
let mut hasher = StableHasher::new();
sub.hash(&mut hasher);
let diagnostic_hash = hasher.finish();
debug!(?diagnostic_hash);
!self.emitted_diagnostics.insert(diagnostic_hash)
};
self.emitter.emit_diagnostic(diagnostic);
diagnostic.children.drain_filter(already_emitted_sub).for_each(|_| {});
self.emitter.emit_diagnostic(diagnostic);
if diagnostic.is_error() {
self.deduplicated_err_count += 1;
} else if let Warning(_) = diagnostic.level {
self.deduplicated_warn_count += 1;
}
}
if diagnostic.is_error() {
self.deduplicated_err_count += 1;
} else if let Warning(_) = diagnostic.level {
self.deduplicated_warn_count += 1;
}
}
if diagnostic.is_error() {
if matches!(diagnostic.level, Level::Error { lint: true }) {
self.bump_lint_err_count();
if matches!(diagnostic.level, Level::Error { lint: true }) {
self.bump_lint_err_count();
} else {
self.bump_err_count();
}
guaranteed = Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
} else {
self.bump_err_count();
self.bump_warn_count();
}
});
Some(ErrorGuaranteed::unchecked_claim_error_was_emitted())
} else {
self.bump_warn_count();
None
}
guaranteed
}
fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) {