mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Auto merge of #121415 - matthiaskrgr:rollup-o9zzet4, r=matthiaskrgr
Rollup of 8 pull requests Successful merges: - #121206 (Top level error handling) - #121261 (coverage: Remove `pending_dups` from the span refiner) - #121336 (triagebot: add queue notifications) - #121373 (Consistently refer to a test's `revision` instead of `cfg`) - #121391 (never patterns: Fix liveness analysis in the presence of never patterns) - #121392 (Unify dylib loading between proc macros and codegen backends) - #121399 (Solaris linker does not support --strip-debug) - #121406 (Add a couple tests) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
d8b00690ec
@ -4048,7 +4048,6 @@ dependencies = [
|
||||
name = "rustc_interface"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
"rustc-rayon",
|
||||
"rustc-rayon-core",
|
||||
"rustc_ast",
|
||||
|
@ -3,7 +3,7 @@ use rustc_ast::CRATE_NODE_ID;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||
use rustc_errors::{DiagCtxt, ErrorGuaranteed};
|
||||
use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError};
|
||||
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
|
||||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use rustc_metadata::find_native_static_library;
|
||||
@ -487,7 +487,9 @@ fn collate_raw_dylibs<'a, 'b>(
|
||||
}
|
||||
}
|
||||
}
|
||||
sess.compile_status()?;
|
||||
if let Some(guar) = sess.dcx().has_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
Ok(dylib_table
|
||||
.into_iter()
|
||||
.map(|(name, imports)| {
|
||||
@ -720,10 +722,7 @@ fn link_dwarf_object<'a>(
|
||||
Ok(())
|
||||
}) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
sess.dcx().emit_err(errors::ThorinErrorWrapper(e));
|
||||
sess.dcx().abort_if_errors();
|
||||
}
|
||||
Err(e) => sess.dcx().emit_fatal(errors::ThorinErrorWrapper(e)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -999,7 +998,7 @@ fn link_natively<'a>(
|
||||
sess.dcx().emit_note(errors::CheckInstalledVisualStudio);
|
||||
sess.dcx().emit_note(errors::InsufficientVSCodeProduct);
|
||||
}
|
||||
sess.dcx().abort_if_errors();
|
||||
FatalError.raise();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,7 +626,7 @@ impl<'a> Linker for GccLinker<'a> {
|
||||
// it does support --strip-all as a compatibility alias for -s.
|
||||
// The --strip-debug case is handled by running an external
|
||||
// `strip` utility as a separate step after linking.
|
||||
if self.sess.target.os != "illumos" {
|
||||
if !self.sess.target.is_like_solaris {
|
||||
self.linker_arg("--strip-debug");
|
||||
}
|
||||
}
|
||||
|
@ -449,10 +449,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
let Some(llfn) = cx.declare_c_main(llfty) else {
|
||||
// FIXME: We should be smart and show a better diagnostic here.
|
||||
let span = cx.tcx().def_span(rust_main_def_id);
|
||||
let dcx = cx.tcx().dcx();
|
||||
dcx.emit_err(errors::MultipleMainFunctions { span });
|
||||
dcx.abort_if_errors();
|
||||
bug!();
|
||||
cx.tcx().dcx().emit_fatal(errors::MultipleMainFunctions { span });
|
||||
};
|
||||
|
||||
// `main` should respect same config for frame pointer elimination as rest of code
|
||||
|
@ -144,16 +144,6 @@ pub const EXIT_FAILURE: i32 = 1;
|
||||
pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
|
||||
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
|
||||
|
||||
pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T {
|
||||
match result {
|
||||
Err(..) => {
|
||||
sess.dcx().abort_if_errors();
|
||||
panic!("error reported but abort_if_errors didn't abort???");
|
||||
}
|
||||
Ok(x) => x,
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Callbacks {
|
||||
/// Called before creating the compiler instance
|
||||
fn config(&mut self, _config: &mut interface::Config) {}
|
||||
@ -349,27 +339,33 @@ fn run_compiler(
|
||||
},
|
||||
};
|
||||
|
||||
callbacks.config(&mut config);
|
||||
|
||||
default_early_dcx.abort_if_errors();
|
||||
drop(default_early_dcx);
|
||||
|
||||
callbacks.config(&mut config);
|
||||
|
||||
interface::run_compiler(config, |compiler| {
|
||||
let sess = &compiler.sess;
|
||||
let codegen_backend = &*compiler.codegen_backend;
|
||||
|
||||
// This is used for early exits unrelated to errors. E.g. when just
|
||||
// printing some information without compiling, or exiting immediately
|
||||
// after parsing, etc.
|
||||
let early_exit = || {
|
||||
if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) }
|
||||
};
|
||||
|
||||
// This implements `-Whelp`. It should be handled very early, like
|
||||
// `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because
|
||||
// it must happen after lints are registered, during session creation.
|
||||
if sess.opts.describe_lints {
|
||||
describe_lints(sess);
|
||||
return sess.compile_status();
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
let early_dcx = EarlyDiagCtxt::new(sess.opts.error_format);
|
||||
|
||||
if print_crate_info(&early_dcx, codegen_backend, sess, has_input) == Compilation::Stop {
|
||||
return sess.compile_status();
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
if !has_input {
|
||||
@ -378,16 +374,16 @@ fn run_compiler(
|
||||
|
||||
if !sess.opts.unstable_opts.ls.is_empty() {
|
||||
list_metadata(&early_dcx, sess, &*codegen_backend.metadata_loader());
|
||||
return sess.compile_status();
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
if sess.opts.unstable_opts.link_only {
|
||||
process_rlink(sess, compiler);
|
||||
return sess.compile_status();
|
||||
return early_exit();
|
||||
}
|
||||
|
||||
let linker = compiler.enter(|queries| {
|
||||
let early_exit = || sess.compile_status().map(|_| None);
|
||||
let early_exit = || early_exit().map(|_| None);
|
||||
queries.parse()?;
|
||||
|
||||
if let Some(ppm) = &sess.opts.pretty {
|
||||
@ -659,10 +655,11 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) {
|
||||
};
|
||||
}
|
||||
};
|
||||
let result = compiler.codegen_backend.link(sess, codegen_results, &outputs);
|
||||
abort_on_err(result, sess);
|
||||
if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() {
|
||||
FatalError.raise();
|
||||
}
|
||||
} else {
|
||||
dcx.emit_fatal(RlinkNotAFile {})
|
||||
dcx.emit_fatal(RlinkNotAFile {});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast_pretty::pprust as pprust_ast;
|
||||
use rustc_errors::FatalError;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir_pretty as pprust_hir;
|
||||
use rustc_middle::bug;
|
||||
@ -18,7 +19,6 @@ use std::fmt::Write;
|
||||
|
||||
pub use self::PpMode::*;
|
||||
pub use self::PpSourceMode::*;
|
||||
use crate::abort_on_err;
|
||||
|
||||
struct AstNoAnn;
|
||||
|
||||
@ -243,7 +243,9 @@ impl<'tcx> PrintExtra<'tcx> {
|
||||
|
||||
pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
|
||||
if ppm.needs_analysis() {
|
||||
abort_on_err(ex.tcx().analysis(()), sess);
|
||||
if ex.tcx().analysis(()).is_err() {
|
||||
FatalError.raise();
|
||||
}
|
||||
}
|
||||
|
||||
let (src, src_name) = get_source(sess);
|
||||
@ -334,7 +336,9 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
|
||||
ThirTree => {
|
||||
let tcx = ex.tcx();
|
||||
let mut out = String::new();
|
||||
abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
|
||||
if rustc_hir_analysis::check_crate(tcx).is_err() {
|
||||
FatalError.raise();
|
||||
}
|
||||
debug!("pretty printing THIR tree");
|
||||
for did in tcx.hir().body_owners() {
|
||||
let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_tree(did));
|
||||
@ -344,7 +348,9 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
|
||||
ThirFlat => {
|
||||
let tcx = ex.tcx();
|
||||
let mut out = String::new();
|
||||
abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
|
||||
if rustc_hir_analysis::check_crate(tcx).is_err() {
|
||||
FatalError.raise();
|
||||
}
|
||||
debug!("pretty printing THIR flat");
|
||||
for did in tcx.hir().body_owners() {
|
||||
let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_flat(did));
|
||||
|
@ -471,9 +471,10 @@ struct DiagCtxtInner {
|
||||
emitted_diagnostics: FxHashSet<Hash128>,
|
||||
|
||||
/// Stashed diagnostics emitted in one stage of the compiler that may be
|
||||
/// stolen by other stages (e.g. to improve them and add more information).
|
||||
/// The stashed diagnostics count towards the total error count.
|
||||
/// When `.abort_if_errors()` is called, these are also emitted.
|
||||
/// stolen and emitted/cancelled by other stages (e.g. to improve them and
|
||||
/// add more information). All stashed diagnostics must be emitted with
|
||||
/// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
|
||||
/// otherwise an assertion failure will occur.
|
||||
stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>,
|
||||
|
||||
future_breakage_diagnostics: Vec<Diagnostic>,
|
||||
@ -558,7 +559,9 @@ pub struct DiagCtxtFlags {
|
||||
|
||||
impl Drop for DiagCtxtInner {
|
||||
fn drop(&mut self) {
|
||||
self.emit_stashed_diagnostics();
|
||||
// Any stashed diagnostics should have been handled by
|
||||
// `emit_stashed_diagnostics` by now.
|
||||
assert!(self.stashed_diagnostics.is_empty());
|
||||
|
||||
if self.err_guars.is_empty() {
|
||||
self.flush_delayed()
|
||||
@ -750,17 +753,24 @@ impl DiagCtxt {
|
||||
}
|
||||
|
||||
/// Emit all stashed diagnostics.
|
||||
pub fn emit_stashed_diagnostics(&self) {
|
||||
pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow_mut().emit_stashed_diagnostics()
|
||||
}
|
||||
|
||||
/// This excludes lint errors, delayed bugs, and stashed errors.
|
||||
/// This excludes lint errors, delayed bugs and stashed errors.
|
||||
#[inline]
|
||||
pub fn err_count(&self) -> usize {
|
||||
pub fn err_count_excluding_lint_errs(&self) -> usize {
|
||||
self.inner.borrow().err_guars.len()
|
||||
}
|
||||
|
||||
/// This excludes normal errors, lint errors and delayed bugs. Unless
|
||||
/// This excludes delayed bugs and stashed errors.
|
||||
#[inline]
|
||||
pub fn err_count(&self) -> usize {
|
||||
let inner = self.inner.borrow();
|
||||
inner.err_guars.len() + inner.lint_err_guars.len()
|
||||
}
|
||||
|
||||
/// This excludes normal errors, lint errors, and delayed bugs. Unless
|
||||
/// absolutely necessary, avoid using this. It's dubious because stashed
|
||||
/// errors can later be cancelled, so the presence of a stashed error at
|
||||
/// some point of time doesn't guarantee anything -- there are no
|
||||
@ -769,27 +779,29 @@ impl DiagCtxt {
|
||||
self.inner.borrow().stashed_err_count
|
||||
}
|
||||
|
||||
/// This excludes lint errors, delayed bugs, and stashed errors.
|
||||
/// This excludes lint errors, delayed bugs, and stashed errors. Unless
|
||||
/// absolutely necessary, prefer `has_errors` to this method.
|
||||
pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_excluding_lint_errors()
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors.
|
||||
pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors()
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors. Unless absolutely
|
||||
/// necessary, prefer `has_errors` to this method.
|
||||
pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_or_lint_errors()
|
||||
}
|
||||
|
||||
/// This excludes stashed errors. Unless absolutely necessary, prefer
|
||||
/// `has_errors` or `has_errors_or_lint_errors` to this method.
|
||||
pub fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_or_lint_errors_or_delayed_bugs()
|
||||
/// `has_errors` to this method.
|
||||
pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
self.inner.borrow().has_errors_or_delayed_bugs()
|
||||
}
|
||||
|
||||
pub fn print_error_count(&self, registry: &Registry) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
|
||||
inner.emit_stashed_diagnostics();
|
||||
// Any stashed diagnostics should have been handled by
|
||||
// `emit_stashed_diagnostics` by now.
|
||||
assert!(inner.stashed_diagnostics.is_empty());
|
||||
|
||||
if inner.treat_err_as_bug() {
|
||||
return;
|
||||
@ -864,10 +876,12 @@ impl DiagCtxt {
|
||||
}
|
||||
}
|
||||
|
||||
/// This excludes delayed bugs and stashed errors. Used for early aborts
|
||||
/// after errors occurred -- e.g. because continuing in the face of errors is
|
||||
/// likely to lead to bad results, such as spurious/uninteresting
|
||||
/// additional errors -- when returning an error `Result` is difficult.
|
||||
pub fn abort_if_errors(&self) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
inner.emit_stashed_diagnostics();
|
||||
if !inner.err_guars.is_empty() {
|
||||
if self.has_errors().is_some() {
|
||||
FatalError.raise();
|
||||
}
|
||||
}
|
||||
@ -1268,10 +1282,10 @@ impl DiagCtxt {
|
||||
// `DiagCtxtInner::foo`.
|
||||
impl DiagCtxtInner {
|
||||
/// Emit all stashed diagnostics.
|
||||
fn emit_stashed_diagnostics(&mut self) {
|
||||
fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
|
||||
let mut guar = None;
|
||||
let has_errors = !self.err_guars.is_empty();
|
||||
for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
|
||||
// Decrement the count tracking the stash; emitting will increment it.
|
||||
if diag.is_error() {
|
||||
if diag.is_lint.is_none() {
|
||||
self.stashed_err_count -= 1;
|
||||
@ -1284,8 +1298,9 @@ impl DiagCtxtInner {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
self.emit_diagnostic(diag);
|
||||
guar = guar.or(self.emit_diagnostic(diag));
|
||||
}
|
||||
guar
|
||||
}
|
||||
|
||||
// Return value is only `Some` if the level is `Error` or `DelayedBug`.
|
||||
@ -1329,7 +1344,7 @@ impl DiagCtxtInner {
|
||||
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_or_lint_errors() {
|
||||
return if let Some(guar) = self.has_errors() {
|
||||
Some(guar)
|
||||
} else {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
@ -1445,17 +1460,16 @@ impl DiagCtxtInner {
|
||||
.is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get())
|
||||
}
|
||||
|
||||
fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.err_guars.get(0).copied()
|
||||
}
|
||||
|
||||
fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.has_errors().or_else(|| self.lint_err_guars.get(0).copied())
|
||||
fn has_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied())
|
||||
}
|
||||
|
||||
fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
self.has_errors_or_lint_errors()
|
||||
.or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
|
||||
fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
|
||||
self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
|
||||
}
|
||||
|
||||
/// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`.
|
||||
@ -1488,6 +1502,11 @@ impl DiagCtxtInner {
|
||||
}
|
||||
|
||||
fn flush_delayed(&mut self) {
|
||||
// Stashed diagnostics must be emitted before delayed bugs are flushed.
|
||||
// Otherwise, we might ICE prematurely when errors would have
|
||||
// eventually happened.
|
||||
assert!(self.stashed_diagnostics.is_empty());
|
||||
|
||||
if self.delayed_bugs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -71,14 +71,21 @@ impl hir::Pat<'_> {
|
||||
/// Call `f` on every "binding" in a pattern, e.g., on `a` in
|
||||
/// `match foo() { Some(a) => (), None => () }`.
|
||||
///
|
||||
/// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited.
|
||||
/// When encountering an or-pattern `p_0 | ... | p_n` only the first non-never pattern will be
|
||||
/// visited. If they're all never patterns we visit nothing, which is ok since a never pattern
|
||||
/// cannot have bindings.
|
||||
pub fn each_binding_or_first(
|
||||
&self,
|
||||
f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident),
|
||||
) {
|
||||
self.walk(|p| match &p.kind {
|
||||
PatKind::Or(ps) => {
|
||||
ps[0].each_binding_or_first(f);
|
||||
for p in *ps {
|
||||
if !p.is_never_pattern() {
|
||||
p.each_binding_or_first(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
PatKind::Binding(bm, _, ident, _) => {
|
||||
|
@ -312,7 +312,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
|
||||
|
||||
let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone();
|
||||
|
||||
if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() {
|
||||
if sess.dcx().has_errors_or_delayed_bugs().is_some() {
|
||||
// If there have been any errors during compilation, we don't want to
|
||||
// publish this session directory. Rather, we'll just delete it.
|
||||
|
||||
|
@ -32,7 +32,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
|
||||
return;
|
||||
}
|
||||
// This is going to be deleted in finalize_session_directory, so let's not create it.
|
||||
if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() {
|
||||
if sess.dcx().has_errors_or_delayed_bugs().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ pub fn save_work_product_index(
|
||||
return;
|
||||
}
|
||||
// This is going to be deleted in finalize_session_directory, so let's not create it
|
||||
if sess.dcx().has_errors_or_lint_errors().is_some() {
|
||||
if sess.dcx().has_errors().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -712,7 +712,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||
reported_trait_errors: Default::default(),
|
||||
reported_signature_mismatch: Default::default(),
|
||||
tainted_by_errors: Cell::new(None),
|
||||
err_count_on_creation: tcx.dcx().err_count(),
|
||||
err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(),
|
||||
stashed_err_count_on_creation: tcx.dcx().stashed_err_count(),
|
||||
universe: Cell::new(ty::UniverseIndex::ROOT),
|
||||
intercrate,
|
||||
@ -1267,8 +1267,11 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
|
||||
if let Some(guar) = self.tainted_by_errors.get() {
|
||||
Some(guar)
|
||||
} else if self.dcx().err_count() > self.err_count_on_creation {
|
||||
// Errors reported since this infcx was made.
|
||||
} else if self.dcx().err_count_excluding_lint_errs() > self.err_count_on_creation {
|
||||
// Errors reported since this infcx was made. Lint errors are
|
||||
// excluded to avoid some being swallowed in the presence of
|
||||
// non-lint errors. (It's arguable whether or not this exclusion is
|
||||
// important.)
|
||||
let guar = self.dcx().has_errors().unwrap();
|
||||
self.set_tainted_by_errors(guar);
|
||||
Some(guar)
|
||||
|
@ -5,7 +5,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
libloading = "0.8.0"
|
||||
rustc-rayon = { version = "0.5.0", optional = true }
|
||||
rustc-rayon-core = { version = "0.5.0", optional = true }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
|
@ -423,18 +423,43 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
||||
Compiler { sess, codegen_backend, override_queries: config.override_queries };
|
||||
|
||||
rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
|
||||
let r = {
|
||||
let _sess_abort_error = defer(|| {
|
||||
compiler.sess.finish_diagnostics(&config.registry);
|
||||
// There are two paths out of `f`.
|
||||
// - Normal exit.
|
||||
// - Panic, e.g. triggered by `abort_if_errors`.
|
||||
//
|
||||
// We must run `finish_diagnostics` in both cases.
|
||||
let res = {
|
||||
// If `f` panics, `finish_diagnostics` will run during
|
||||
// unwinding because of the `defer`.
|
||||
let mut guar = None;
|
||||
let sess_abort_guard = defer(|| {
|
||||
guar = compiler.sess.finish_diagnostics(&config.registry);
|
||||
});
|
||||
|
||||
f(&compiler)
|
||||
let res = f(&compiler);
|
||||
|
||||
// If `f` doesn't panic, `finish_diagnostics` will run
|
||||
// normally when `sess_abort_guard` is dropped.
|
||||
drop(sess_abort_guard);
|
||||
|
||||
// If `finish_diagnostics` emits errors (e.g. stashed
|
||||
// errors) we can't return an error directly, because the
|
||||
// return type of this function is `R`, not `Result<R, E>`.
|
||||
// But we need to communicate the errors' existence to the
|
||||
// caller, otherwise the caller might mistakenly think that
|
||||
// no errors occurred and return a zero exit code. So we
|
||||
// abort (panic) instead, similar to if `f` had panicked.
|
||||
if guar.is_some() {
|
||||
compiler.sess.dcx().abort_if_errors();
|
||||
}
|
||||
|
||||
res
|
||||
};
|
||||
|
||||
let prof = compiler.sess.prof.clone();
|
||||
|
||||
prof.generic_activity("drop_compiler").run(move || drop(compiler));
|
||||
r
|
||||
|
||||
res
|
||||
})
|
||||
},
|
||||
)
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![feature(decl_macro)]
|
||||
#![feature(error_iter)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(lazy_cell)]
|
||||
#![feature(let_chains)]
|
||||
|
@ -772,12 +772,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
||||
// lot of annoying errors in the ui tests (basically,
|
||||
// lint warnings and so on -- kindck used to do this abort, but
|
||||
// kindck is gone now). -nmatsakis
|
||||
if let Some(reported) = sess.dcx().has_errors() {
|
||||
return Err(reported);
|
||||
} else if sess.dcx().stashed_err_count() > 0 {
|
||||
// Without this case we sometimes get delayed bug ICEs and I don't
|
||||
// understand why. -nnethercote
|
||||
return Err(sess.dcx().delayed_bug("some stashed error is waiting for use"));
|
||||
//
|
||||
// But we exclude lint errors from this, because lint errors are typically
|
||||
// less serious and we're more likely to want to continue (#87337).
|
||||
if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
sess.time("misc_checking_3", || {
|
||||
@ -937,9 +936,7 @@ pub fn start_codegen<'tcx>(
|
||||
|
||||
if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) {
|
||||
if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) {
|
||||
let dcx = tcx.dcx();
|
||||
dcx.emit_err(errors::CantEmitMIR { error });
|
||||
dcx.abort_if_errors();
|
||||
tcx.dcx().emit_fatal(errors::CantEmitMIR { error });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,12 +222,12 @@ impl<'tcx> Queries<'tcx> {
|
||||
|
||||
pub fn codegen_and_build_linker(&'tcx self) -> Result<Linker> {
|
||||
self.global_ctxt()?.enter(|tcx| {
|
||||
// Don't do code generation if there were any errors
|
||||
self.compiler.sess.compile_status()?;
|
||||
|
||||
// If we have any delayed bugs, for example because we created TyKind::Error earlier,
|
||||
// it's likely that codegen will only cause more ICEs, obscuring the original problem
|
||||
self.compiler.sess.dcx().flush_delayed();
|
||||
// Don't do code generation if there were any errors. Likewise if
|
||||
// there were any delayed bugs, because codegen will likely cause
|
||||
// more ICEs, obscuring the original problem.
|
||||
if let Some(guar) = self.compiler.sess.dcx().has_errors_or_delayed_bugs() {
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
// Hook for UI tests.
|
||||
Self::check_for_rustc_errors_attr(tcx);
|
||||
@ -261,7 +261,9 @@ impl Linker {
|
||||
let (codegen_results, work_products) =
|
||||
codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames);
|
||||
|
||||
sess.compile_status()?;
|
||||
if let Some(guar) = sess.dcx().has_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
sess.time("serialize_work_products", || {
|
||||
rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products)
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::errors;
|
||||
use info;
|
||||
use libloading::Library;
|
||||
use rustc_ast as ast;
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
#[cfg(parallel_compiler)]
|
||||
use rustc_data_structures::sync;
|
||||
use rustc_metadata::{load_symbol_from_dylib, DylibError};
|
||||
use rustc_parse::validate_attr;
|
||||
use rustc_session as session;
|
||||
use rustc_session::config::{self, Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes};
|
||||
@ -17,7 +17,6 @@ use rustc_span::symbol::{sym, Symbol};
|
||||
use session::EarlyDiagCtxt;
|
||||
use std::env;
|
||||
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::OnceLock;
|
||||
@ -162,29 +161,19 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
|
||||
}
|
||||
|
||||
fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn {
|
||||
fn format_err(e: &(dyn std::error::Error + 'static)) -> String {
|
||||
e.sources().map(|e| format!(": {e}")).collect()
|
||||
}
|
||||
let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
|
||||
let err = format!("couldn't load codegen backend {path:?}{}", format_err(&err));
|
||||
early_dcx.early_fatal(err);
|
||||
});
|
||||
|
||||
let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
|
||||
.unwrap_or_else(|e| {
|
||||
match unsafe { load_symbol_from_dylib::<MakeBackendFn>(path, "__rustc_codegen_backend") } {
|
||||
Ok(backend_sym) => backend_sym,
|
||||
Err(DylibError::DlOpen(path, err)) => {
|
||||
let err = format!("couldn't load codegen backend {path}{err}");
|
||||
early_dcx.early_fatal(err);
|
||||
}
|
||||
Err(DylibError::DlSym(_path, err)) => {
|
||||
let e = format!(
|
||||
"`__rustc_codegen_backend` symbol lookup in the codegen backend failed{}",
|
||||
format_err(&e)
|
||||
"`__rustc_codegen_backend` symbol lookup in the codegen backend failed{err}",
|
||||
);
|
||||
early_dcx.early_fatal(e);
|
||||
});
|
||||
|
||||
// Intentionally leak the dynamic library. We can't ever unload it
|
||||
// since the library can make things that will live arbitrarily long.
|
||||
let backend_sym = unsafe { backend_sym.into_raw() };
|
||||
mem::forget(lib);
|
||||
|
||||
*backend_sym
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the codegen backend based on the name and specified sysroot.
|
||||
|
@ -45,7 +45,7 @@ metadata_crate_not_panic_runtime =
|
||||
the crate `{$crate_name}` is not a panic runtime
|
||||
|
||||
metadata_dl_error =
|
||||
{$err}
|
||||
{$path}{$err}
|
||||
|
||||
metadata_empty_link_name =
|
||||
link name must not be empty
|
||||
|
@ -692,20 +692,8 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
|
||||
path: &Path,
|
||||
stable_crate_id: StableCrateId,
|
||||
) -> Result<&'static [ProcMacro], CrateError> {
|
||||
// Make sure the path contains a / or the linker will search for it.
|
||||
let path = try_canonicalize(path).unwrap();
|
||||
let lib = load_dylib(&path, 5).map_err(|err| CrateError::DlOpen(err))?;
|
||||
|
||||
let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id);
|
||||
let sym = unsafe { lib.get::<*const &[ProcMacro]>(sym_name.as_bytes()) }
|
||||
.map_err(|err| CrateError::DlSym(err.to_string()))?;
|
||||
|
||||
// Intentionally leak the dynamic library. We can't ever unload it
|
||||
// since the library can make things that will live arbitrarily long.
|
||||
let sym = unsafe { sym.into_raw() };
|
||||
std::mem::forget(lib);
|
||||
|
||||
Ok(unsafe { **sym })
|
||||
Ok(unsafe { *load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name)? })
|
||||
}
|
||||
|
||||
fn inject_panic_runtime(&mut self, krate: &ast::Crate) {
|
||||
@ -926,7 +914,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
|
||||
what: &str,
|
||||
needs_dep: &dyn Fn(&CrateMetadata) -> bool,
|
||||
) {
|
||||
// don't perform this validation if the session has errors, as one of
|
||||
// Don't perform this validation if the session has errors, as one of
|
||||
// those errors may indicate a circular dependency which could cause
|
||||
// this to stack overflow.
|
||||
if self.dcx().has_errors().is_some() {
|
||||
@ -1116,6 +1104,10 @@ fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec<Span> {
|
||||
f.spans
|
||||
}
|
||||
|
||||
fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String {
|
||||
e.sources().map(|e| format!(": {e}")).collect()
|
||||
}
|
||||
|
||||
// On Windows the compiler would sometimes intermittently fail to open the
|
||||
// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
|
||||
// system still holds a lock on the file, so we retry a few times before calling it
|
||||
@ -1154,9 +1146,43 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S
|
||||
|
||||
let last_error = last_error.unwrap();
|
||||
let message = if let Some(src) = last_error.source() {
|
||||
format!("{last_error} ({src}) (retried {max_attempts} times)")
|
||||
format!("{} ({src}) (retried {max_attempts} times)", format_dlopen_err(&last_error))
|
||||
} else {
|
||||
format!("{last_error} (retried {max_attempts} times)")
|
||||
format!("{} (retried {max_attempts} times)", format_dlopen_err(&last_error))
|
||||
};
|
||||
Err(message)
|
||||
}
|
||||
|
||||
pub enum DylibError {
|
||||
DlOpen(String, String),
|
||||
DlSym(String, String),
|
||||
}
|
||||
|
||||
impl From<DylibError> for CrateError {
|
||||
fn from(err: DylibError) -> CrateError {
|
||||
match err {
|
||||
DylibError::DlOpen(path, err) => CrateError::DlOpen(path, err),
|
||||
DylibError::DlSym(path, err) => CrateError::DlSym(path, err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn load_symbol_from_dylib<T: Copy>(
|
||||
path: &Path,
|
||||
sym_name: &str,
|
||||
) -> Result<T, DylibError> {
|
||||
// Make sure the path contains a / or the linker will search for it.
|
||||
let path = try_canonicalize(path).unwrap();
|
||||
let lib =
|
||||
load_dylib(&path, 5).map_err(|err| DylibError::DlOpen(path.display().to_string(), err))?;
|
||||
|
||||
let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) }
|
||||
.map_err(|err| DylibError::DlSym(path.display().to_string(), format_dlopen_err(&err)))?;
|
||||
|
||||
// Intentionally leak the dynamic library. We can't ever unload it
|
||||
// since the library can make things that will live arbitrarily long.
|
||||
let sym = unsafe { sym.into_raw() };
|
||||
std::mem::forget(lib);
|
||||
|
||||
Ok(*sym)
|
||||
}
|
||||
|
@ -535,6 +535,7 @@ pub struct StableCrateIdCollision {
|
||||
pub struct DlError {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub path: String,
|
||||
pub err: String,
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#![feature(rustdoc_internals)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(error_iter)]
|
||||
#![feature(extract_if)]
|
||||
#![feature(coroutines)]
|
||||
#![feature(generic_nonzero)]
|
||||
@ -39,6 +40,7 @@ pub mod errors;
|
||||
pub mod fs;
|
||||
pub mod locator;
|
||||
|
||||
pub use creader::{load_symbol_from_dylib, DylibError};
|
||||
pub use fs::{emit_wrapper_file, METADATA_FILENAME};
|
||||
pub use native_libs::find_native_static_library;
|
||||
pub use rmeta::{encode_metadata, rendered_const, EncodedMetadata, METADATA_HEADER};
|
||||
|
@ -921,8 +921,8 @@ pub(crate) enum CrateError {
|
||||
MultipleCandidates(Symbol, CrateFlavor, Vec<PathBuf>),
|
||||
SymbolConflictsCurrent(Symbol),
|
||||
StableCrateIdCollision(Symbol, Symbol),
|
||||
DlOpen(String),
|
||||
DlSym(String),
|
||||
DlOpen(String, String),
|
||||
DlSym(String, String),
|
||||
LocatorCombined(Box<CombinedLocatorError>),
|
||||
NotFound(Symbol),
|
||||
}
|
||||
@ -967,8 +967,8 @@ impl CrateError {
|
||||
CrateError::StableCrateIdCollision(crate_name0, crate_name1) => {
|
||||
dcx.emit_err(errors::StableCrateIdCollision { span, crate_name0, crate_name1 });
|
||||
}
|
||||
CrateError::DlOpen(s) | CrateError::DlSym(s) => {
|
||||
dcx.emit_err(errors::DlError { span, err: s });
|
||||
CrateError::DlOpen(path, err) | CrateError::DlSym(path, err) => {
|
||||
dcx.emit_err(errors::DlError { span, path, err });
|
||||
}
|
||||
CrateError::LocatorCombined(locator) => {
|
||||
let crate_name = locator.crate_name;
|
||||
|
@ -153,11 +153,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
) -> Self::Const {
|
||||
Const::new_bound(self, debruijn, var, ty)
|
||||
}
|
||||
|
||||
fn expect_error_or_delayed_bug() {
|
||||
let has_errors = ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs());
|
||||
assert!(has_errors.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
|
||||
|
@ -61,7 +61,7 @@ pub(super) fn generate_coverage_spans(
|
||||
hir_info,
|
||||
basic_coverage_blocks,
|
||||
);
|
||||
let coverage_spans = SpansRefiner::refine_sorted_spans(basic_coverage_blocks, sorted_spans);
|
||||
let coverage_spans = SpansRefiner::refine_sorted_spans(sorted_spans);
|
||||
mappings.extend(coverage_spans.into_iter().map(|RefinedCovspan { bcb, span, .. }| {
|
||||
// Each span produced by the generator represents an ordinary code region.
|
||||
BcbMapping { kind: BcbMappingKind::Code(bcb), span }
|
||||
@ -88,8 +88,6 @@ pub(super) fn generate_coverage_spans(
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CurrCovspan {
|
||||
/// This is used as the basis for [`PrevCovspan::original_span`], so it must
|
||||
/// not be modified.
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
@ -102,7 +100,7 @@ impl CurrCovspan {
|
||||
|
||||
fn into_prev(self) -> PrevCovspan {
|
||||
let Self { span, bcb, is_closure } = self;
|
||||
PrevCovspan { original_span: span, span, bcb, merged_spans: vec![span], is_closure }
|
||||
PrevCovspan { span, bcb, merged_spans: vec![span], is_closure }
|
||||
}
|
||||
|
||||
fn into_refined(self) -> RefinedCovspan {
|
||||
@ -115,7 +113,6 @@ impl CurrCovspan {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PrevCovspan {
|
||||
original_span: Span,
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
/// List of all the original spans from MIR that have been merged into this
|
||||
@ -135,42 +132,17 @@ impl PrevCovspan {
|
||||
self.merged_spans.push(other.span);
|
||||
}
|
||||
|
||||
fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
|
||||
fn cutoff_statements_at(mut self, cutoff_pos: BytePos) -> Option<RefinedCovspan> {
|
||||
self.merged_spans.retain(|span| span.hi() <= cutoff_pos);
|
||||
if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() {
|
||||
self.span = self.span.with_hi(max_hi);
|
||||
}
|
||||
}
|
||||
|
||||
fn into_dup(self) -> DuplicateCovspan {
|
||||
let Self { original_span, span, bcb, merged_spans: _, is_closure } = self;
|
||||
// Only unmodified spans end up in `pending_dups`.
|
||||
debug_assert_eq!(original_span, span);
|
||||
DuplicateCovspan { span, bcb, is_closure }
|
||||
if self.merged_spans.is_empty() { None } else { Some(self.into_refined()) }
|
||||
}
|
||||
|
||||
fn refined_copy(&self) -> RefinedCovspan {
|
||||
let &Self { original_span: _, span, bcb, merged_spans: _, is_closure } = self;
|
||||
RefinedCovspan { span, bcb, is_closure }
|
||||
}
|
||||
|
||||
fn into_refined(self) -> RefinedCovspan {
|
||||
self.refined_copy()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DuplicateCovspan {
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
}
|
||||
|
||||
impl DuplicateCovspan {
|
||||
/// Returns a copy of this covspan, as a [`RefinedCovspan`].
|
||||
/// Should only be called in places that would otherwise clone this covspan.
|
||||
fn refined_copy(&self) -> RefinedCovspan {
|
||||
let &Self { span, bcb, is_closure } = self;
|
||||
let &Self { span, bcb, merged_spans: _, is_closure } = self;
|
||||
RefinedCovspan { span, bcb, is_closure }
|
||||
}
|
||||
|
||||
@ -205,10 +177,7 @@ impl RefinedCovspan {
|
||||
/// * Merge spans that represent continuous (both in source code and control flow), non-branching
|
||||
/// execution
|
||||
/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures)
|
||||
struct SpansRefiner<'a> {
|
||||
/// The BasicCoverageBlock Control Flow Graph (BCB CFG).
|
||||
basic_coverage_blocks: &'a CoverageGraph,
|
||||
|
||||
struct SpansRefiner {
|
||||
/// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative
|
||||
/// dominance between the `BasicCoverageBlock`s of equal `Span`s.
|
||||
sorted_spans_iter: std::vec::IntoIter<SpanFromMir>,
|
||||
@ -223,36 +192,22 @@ struct SpansRefiner<'a> {
|
||||
/// If that `curr` was discarded, `prev` retains its value from the previous iteration.
|
||||
some_prev: Option<PrevCovspan>,
|
||||
|
||||
/// One or more coverage spans with the same `Span` but different `BasicCoverageBlock`s, and
|
||||
/// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list.
|
||||
/// If a new `curr` span also fits this criteria (compared to an existing list of
|
||||
/// `pending_dups`), that `curr` moves to `prev` before possibly being added to
|
||||
/// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups`
|
||||
/// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev`
|
||||
/// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a
|
||||
/// `prev` with a matching `Span`)
|
||||
pending_dups: Vec<DuplicateCovspan>,
|
||||
|
||||
/// The final coverage spans to add to the coverage map. A `Counter` or `Expression`
|
||||
/// will also be injected into the MIR for each BCB that has associated spans.
|
||||
refined_spans: Vec<RefinedCovspan>,
|
||||
}
|
||||
|
||||
impl<'a> SpansRefiner<'a> {
|
||||
impl SpansRefiner {
|
||||
/// Takes the initial list of (sorted) spans extracted from MIR, and "refines"
|
||||
/// them by merging compatible adjacent spans, removing redundant spans,
|
||||
/// and carving holes in spans when they overlap in unwanted ways.
|
||||
fn refine_sorted_spans(
|
||||
basic_coverage_blocks: &'a CoverageGraph,
|
||||
sorted_spans: Vec<SpanFromMir>,
|
||||
) -> Vec<RefinedCovspan> {
|
||||
fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
|
||||
let sorted_spans_len = sorted_spans.len();
|
||||
let this = Self {
|
||||
basic_coverage_blocks,
|
||||
sorted_spans_iter: sorted_spans.into_iter(),
|
||||
some_curr: None,
|
||||
some_prev: None,
|
||||
pending_dups: Vec::new(),
|
||||
refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2),
|
||||
refined_spans: Vec::with_capacity(sorted_spans_len),
|
||||
};
|
||||
|
||||
this.to_refined_spans()
|
||||
@ -292,21 +247,11 @@ impl<'a> SpansRefiner<'a> {
|
||||
self.take_curr(); // Discards curr.
|
||||
} else if curr.is_closure {
|
||||
self.carve_out_span_for_closure();
|
||||
} else if prev.original_span == prev.span && prev.span == curr.span {
|
||||
// Prev and curr have the same span, and prev's span hasn't
|
||||
// been modified by other spans.
|
||||
self.update_pending_dups();
|
||||
} else {
|
||||
self.cutoff_prev_at_overlapping_curr();
|
||||
}
|
||||
}
|
||||
|
||||
// Drain any remaining dups into the output.
|
||||
for dup in self.pending_dups.drain(..) {
|
||||
debug!(" ...adding at least one pending dup={:?}", dup);
|
||||
self.refined_spans.push(dup.into_refined());
|
||||
}
|
||||
|
||||
// There is usually a final span remaining in `prev` after the loop ends,
|
||||
// so add it to the output as well.
|
||||
if let Some(prev) = self.some_prev.take() {
|
||||
@ -359,36 +304,6 @@ impl<'a> SpansRefiner<'a> {
|
||||
self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)"))
|
||||
}
|
||||
|
||||
/// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the
|
||||
/// `pending_dups` spans), then one of the following two things happened during the previous
|
||||
/// iteration:
|
||||
/// * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups
|
||||
/// (in which case there should be at least two spans in `pending_dups`); or
|
||||
/// * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case
|
||||
/// `pending_dups` could have as few as one span)
|
||||
/// In either case, no more spans will match the span of `pending_dups`, so
|
||||
/// add the `pending_dups` if they don't overlap `curr`, and clear the list.
|
||||
fn maybe_flush_pending_dups(&mut self) {
|
||||
let Some(last_dup) = self.pending_dups.last() else { return };
|
||||
if last_dup.span == self.prev().span {
|
||||
return;
|
||||
}
|
||||
|
||||
debug!(
|
||||
" SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \
|
||||
previous iteration, or prev started a new disjoint span"
|
||||
);
|
||||
if last_dup.span.hi() <= self.curr().span.lo() {
|
||||
for dup in self.pending_dups.drain(..) {
|
||||
debug!(" ...adding at least one pending={:?}", dup);
|
||||
self.refined_spans.push(dup.into_refined());
|
||||
}
|
||||
} else {
|
||||
self.pending_dups.clear();
|
||||
}
|
||||
assert!(self.pending_dups.is_empty());
|
||||
}
|
||||
|
||||
/// Advance `prev` to `curr` (if any), and `curr` to the next coverage span in sorted order.
|
||||
fn next_coverage_span(&mut self) -> bool {
|
||||
if let Some(curr) = self.some_curr.take() {
|
||||
@ -408,7 +323,6 @@ impl<'a> SpansRefiner<'a> {
|
||||
);
|
||||
} else {
|
||||
self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_closure));
|
||||
self.maybe_flush_pending_dups();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -433,13 +347,6 @@ impl<'a> SpansRefiner<'a> {
|
||||
let mut pre_closure = self.prev().refined_copy();
|
||||
pre_closure.span = pre_closure.span.with_hi(left_cutoff);
|
||||
debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure);
|
||||
|
||||
for mut dup in self.pending_dups.iter().map(DuplicateCovspan::refined_copy) {
|
||||
dup.span = dup.span.with_hi(left_cutoff);
|
||||
debug!(" ...and at least one pre_closure dup={:?}", dup);
|
||||
self.refined_spans.push(dup);
|
||||
}
|
||||
|
||||
self.refined_spans.push(pre_closure);
|
||||
}
|
||||
|
||||
@ -448,58 +355,9 @@ impl<'a> SpansRefiner<'a> {
|
||||
self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
|
||||
debug!(" Mutated prev.span to start after the closure. prev={:?}", self.prev());
|
||||
|
||||
for dup in &mut self.pending_dups {
|
||||
debug!(" ...and at least one overlapping dup={:?}", dup);
|
||||
dup.span = dup.span.with_lo(right_cutoff);
|
||||
}
|
||||
|
||||
// Prevent this curr from becoming prev.
|
||||
let closure_covspan = self.take_curr().into_refined();
|
||||
self.refined_spans.push(closure_covspan); // since self.prev() was already updated
|
||||
} else {
|
||||
self.pending_dups.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Called if `curr.span` equals `prev.original_span` (and potentially equal to all
|
||||
/// `pending_dups` spans, if any). Keep in mind, `prev.span()` may have been changed.
|
||||
/// If prev.span() was merged into other spans (with matching BCB, for instance),
|
||||
/// `prev.span.hi()` will be greater than (further right of) `prev.original_span.hi()`.
|
||||
/// If prev.span() was split off to the right of a closure, prev.span().lo() will be
|
||||
/// greater than prev.original_span.lo(). The actual span of `prev.original_span` is
|
||||
/// not as important as knowing that `prev()` **used to have the same span** as `curr()`,
|
||||
/// which means their sort order is still meaningful for determining the dominator
|
||||
/// relationship.
|
||||
///
|
||||
/// When two coverage spans have the same `Span`, dominated spans can be discarded; but if
|
||||
/// neither coverage span dominates the other, both (or possibly more than two) are held,
|
||||
/// until their disposition is determined. In this latter case, the `prev` dup is moved into
|
||||
/// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration.
|
||||
fn update_pending_dups(&mut self) {
|
||||
let prev_bcb = self.prev().bcb;
|
||||
let curr_bcb = self.curr().bcb;
|
||||
|
||||
// Equal coverage spans are ordered by dominators before dominated (if any), so it should be
|
||||
// impossible for `curr` to dominate any previous coverage span.
|
||||
debug_assert!(!self.basic_coverage_blocks.dominates(curr_bcb, prev_bcb));
|
||||
|
||||
// `prev` is a duplicate of `curr`, so add it to the list of pending dups.
|
||||
// If it dominates `curr`, it will be removed by the subsequent discard step.
|
||||
let prev = self.take_prev().into_dup();
|
||||
debug!(?prev, "adding prev to pending dups");
|
||||
self.pending_dups.push(prev);
|
||||
|
||||
let initial_pending_count = self.pending_dups.len();
|
||||
if initial_pending_count > 0 {
|
||||
self.pending_dups
|
||||
.retain(|dup| !self.basic_coverage_blocks.dominates(dup.bcb, curr_bcb));
|
||||
|
||||
let n_discarded = initial_pending_count - self.pending_dups.len();
|
||||
if n_discarded > 0 {
|
||||
debug!(
|
||||
" discarded {n_discarded} of {initial_pending_count} pending_dups that dominated curr",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,19 +374,13 @@ impl<'a> SpansRefiner<'a> {
|
||||
if it has statements that end before curr; prev={:?}",
|
||||
self.prev()
|
||||
);
|
||||
if self.pending_dups.is_empty() {
|
||||
let curr_span = self.curr().span;
|
||||
self.prev_mut().cutoff_statements_at(curr_span.lo());
|
||||
if self.prev().merged_spans.is_empty() {
|
||||
debug!(" ... no non-overlapping statements to add");
|
||||
} else {
|
||||
debug!(" ... adding modified prev={:?}", self.prev());
|
||||
let prev = self.take_prev().into_refined();
|
||||
self.refined_spans.push(prev);
|
||||
}
|
||||
|
||||
let curr_span = self.curr().span;
|
||||
if let Some(prev) = self.take_prev().cutoff_statements_at(curr_span.lo()) {
|
||||
debug!("after cutoff, adding {prev:?}");
|
||||
self.refined_spans.push(prev);
|
||||
} else {
|
||||
// with `pending_dups`, `prev` cannot have any statements that don't overlap
|
||||
self.pending_dups.clear();
|
||||
debug!("prev was eliminated by cutoff");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,14 +52,19 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
||||
// - Span A extends further left, or
|
||||
// - Both have the same start and span A extends further right
|
||||
.then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
|
||||
// If both spans are equal, sort the BCBs in dominator order,
|
||||
// so that dominating BCBs come before other BCBs they dominate.
|
||||
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb))
|
||||
// If two spans are otherwise identical, put closure spans first,
|
||||
// as this seems to be what the refinement step expects.
|
||||
// If two spans have the same lo & hi, put closure spans first,
|
||||
// as they take precedence over non-closure spans.
|
||||
.then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
|
||||
// After deduplication, we want to keep only the most-dominated BCB.
|
||||
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
|
||||
});
|
||||
|
||||
// Among covspans with the same span, keep only one. Closure spans take
|
||||
// precedence, otherwise keep the one with the most-dominated BCB.
|
||||
// (Ideally we should try to preserve _all_ non-dominating BCBs, but that
|
||||
// requires a lot more complexity in the span refiner, for little benefit.)
|
||||
initial_spans.dedup_by(|b, a| a.span.source_equal(b.span));
|
||||
|
||||
initial_spans
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
) {
|
||||
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
|
||||
ty::Adt(adt, _) => adt.variant_of_res(res),
|
||||
_ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"),
|
||||
_ => {
|
||||
self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len());
|
||||
let first_n = pats.iter().enumerate().take(dotdot);
|
||||
|
@ -526,8 +526,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn define_bindings_in_pat(&mut self, pat: &hir::Pat<'_>, mut succ: LiveNode) -> LiveNode {
|
||||
// In an or-pattern, only consider the first pattern; any later patterns
|
||||
// must have the same bindings, and we also consider the first pattern
|
||||
// In an or-pattern, only consider the first non-never pattern; any later patterns
|
||||
// must have the same bindings, and we also consider that pattern
|
||||
// to be the "authoritative" set of ids.
|
||||
pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| {
|
||||
let ln = self.live_node(hir_id, pat_sp);
|
||||
|
@ -817,7 +817,7 @@ impl<D: Deps> DepGraphData<D> {
|
||||
None => {}
|
||||
}
|
||||
|
||||
if let None = qcx.dep_context().sess().dcx().has_errors_or_lint_errors_or_delayed_bugs() {
|
||||
if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() {
|
||||
panic!("try_mark_previous_green() - Forcing the DepNode should have set its color")
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ use crate::errors::{
|
||||
};
|
||||
use crate::Session;
|
||||
use rustc_ast::{self as ast, attr};
|
||||
use rustc_errors::FatalError;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::path::Path;
|
||||
@ -115,7 +116,7 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
|
||||
}
|
||||
|
||||
if err_count > 0 {
|
||||
sess.dcx().abort_if_errors();
|
||||
FatalError.raise();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,8 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_miri_unleashed_features(&self) {
|
||||
fn check_miri_unleashed_features(&self) -> Option<ErrorGuaranteed> {
|
||||
let mut guar = None;
|
||||
let unleashed_features = self.miri_unleashed_features.lock();
|
||||
if !unleashed_features.is_empty() {
|
||||
let mut must_err = false;
|
||||
@ -279,18 +280,22 @@ impl Session {
|
||||
// If we should err, make sure we did.
|
||||
if must_err && self.dcx().has_errors().is_none() {
|
||||
// We have skipped a feature gate, and not run into other errors... reject.
|
||||
self.dcx().emit_err(errors::NotCircumventFeature);
|
||||
guar = Some(self.dcx().emit_err(errors::NotCircumventFeature));
|
||||
}
|
||||
}
|
||||
guar
|
||||
}
|
||||
|
||||
/// Invoked all the way at the end to finish off diagnostics printing.
|
||||
pub fn finish_diagnostics(&self, registry: &Registry) {
|
||||
self.check_miri_unleashed_features();
|
||||
pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> {
|
||||
let mut guar = None;
|
||||
guar = guar.or(self.check_miri_unleashed_features());
|
||||
guar = guar.or(self.dcx().emit_stashed_diagnostics());
|
||||
self.dcx().print_error_count(registry);
|
||||
if self.opts.json_future_incompat {
|
||||
self.dcx().emit_future_breakage_report();
|
||||
}
|
||||
guar
|
||||
}
|
||||
|
||||
/// Returns true if the crate is a testing one.
|
||||
@ -312,16 +317,6 @@ impl Session {
|
||||
err
|
||||
}
|
||||
|
||||
pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> {
|
||||
// We must include lint errors here.
|
||||
if let Some(reported) = self.dcx().has_errors_or_lint_errors() {
|
||||
self.dcx().emit_stashed_diagnostics();
|
||||
Err(reported)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Record the fact that we called `trimmed_def_paths`, and do some
|
||||
/// checking about whether its cost was justified.
|
||||
pub fn record_trimmed_def_paths(&self) {
|
||||
@ -1410,10 +1405,6 @@ impl EarlyDiagCtxt {
|
||||
Self { dcx: DiagCtxt::with_emitter(emitter) }
|
||||
}
|
||||
|
||||
pub fn abort_if_errors(&self) {
|
||||
self.dcx.abort_if_errors()
|
||||
}
|
||||
|
||||
/// Swap out the underlying dcx once we acquire the user's preference on error emission
|
||||
/// format. Any errors prior to that will cause an abort and all stashed diagnostics of the
|
||||
/// previous dcx will be emitted.
|
||||
|
@ -22,7 +22,7 @@ use crate::traits::{
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||
use rustc_errors::{
|
||||
codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
|
||||
MultiSpan, StashKey, StringPart,
|
||||
FatalError, MultiSpan, StashKey, StringPart,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Namespace, Res};
|
||||
@ -193,14 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit);
|
||||
mutate(&mut err);
|
||||
err.emit();
|
||||
|
||||
self.dcx().abort_if_errors();
|
||||
// FIXME: this should be something like `build_overflow_error_fatal`, which returns
|
||||
// `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`.
|
||||
unreachable!(
|
||||
"did not expect compilation to continue after `abort_if_errors`, \
|
||||
since an error was definitely emitted!"
|
||||
);
|
||||
FatalError.raise();
|
||||
}
|
||||
|
||||
fn build_overflow_error<T>(
|
||||
|
@ -95,9 +95,6 @@ pub trait Interner: Sized {
|
||||
fn mk_bound_ty(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Ty;
|
||||
fn mk_bound_region(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Region;
|
||||
fn mk_bound_const(self, debruijn: DebruijnIndex, var: BoundVar, ty: Self::Ty) -> Self::Const;
|
||||
|
||||
/// Assert that an error has been delayed or emitted.
|
||||
fn expect_error_or_delayed_bug();
|
||||
}
|
||||
|
||||
/// Common capabilities of placeholder kinds
|
||||
|
@ -3,7 +3,7 @@ use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::emitter::{DynEmitter, HumanEmitter};
|
||||
use rustc_errors::json::JsonEmitter;
|
||||
use rustc_errors::{codes::*, TerminalUrl};
|
||||
use rustc_errors::{codes::*, ErrorGuaranteed, TerminalUrl};
|
||||
use rustc_feature::UnstableFeatures;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
|
||||
@ -306,7 +306,7 @@ pub(crate) fn run_global_ctxt(
|
||||
show_coverage: bool,
|
||||
render_options: RenderOptions,
|
||||
output_format: OutputFormat,
|
||||
) -> (clean::Crate, RenderOptions, Cache) {
|
||||
) -> Result<(clean::Crate, RenderOptions, Cache), ErrorGuaranteed> {
|
||||
// Certain queries assume that some checks were run elsewhere
|
||||
// (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425),
|
||||
// so type-check everything other than function bodies in this crate before running lints.
|
||||
@ -331,7 +331,10 @@ pub(crate) fn run_global_ctxt(
|
||||
});
|
||||
});
|
||||
|
||||
tcx.dcx().abort_if_errors();
|
||||
if let Some(guar) = tcx.dcx().has_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx));
|
||||
tcx.sess.time("check_mod_attrs", || {
|
||||
tcx.hir().for_each_module(|module| tcx.ensure().check_mod_attrs(module))
|
||||
@ -452,14 +455,13 @@ pub(crate) fn run_global_ctxt(
|
||||
|
||||
tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc)));
|
||||
|
||||
// We must include lint errors here.
|
||||
if tcx.dcx().has_errors_or_lint_errors().is_some() {
|
||||
rustc_errors::FatalError.raise();
|
||||
if let Some(guar) = tcx.dcx().has_errors() {
|
||||
return Err(guar);
|
||||
}
|
||||
|
||||
krate = tcx.sess.time("create_format_cache", || Cache::populate(&mut ctxt, krate));
|
||||
|
||||
(krate, ctxt.render_options, ctxt.cache)
|
||||
Ok((krate, ctxt.render_options, ctxt.cache))
|
||||
}
|
||||
|
||||
/// Due to <https://github.com/rust-lang/rust/pull/73566>,
|
||||
|
@ -153,8 +153,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
|
||||
|
||||
collector
|
||||
});
|
||||
// We must include lint errors here.
|
||||
if compiler.sess.dcx().has_errors_or_lint_errors().is_some() {
|
||||
if compiler.sess.dcx().has_errors().is_some() {
|
||||
FatalError.raise();
|
||||
}
|
||||
|
||||
|
@ -78,8 +78,7 @@ use std::io::{self, IsTerminal};
|
||||
use std::process;
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
use rustc_driver::abort_on_err;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_errors::{ErrorGuaranteed, FatalError};
|
||||
use rustc_interface::interface;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup};
|
||||
@ -779,7 +778,7 @@ fn main_args(
|
||||
}
|
||||
|
||||
compiler.enter(|queries| {
|
||||
let mut gcx = abort_on_err(queries.global_ctxt(), sess);
|
||||
let Ok(mut gcx) = queries.global_ctxt() else { FatalError.raise() };
|
||||
if sess.dcx().has_errors().is_some() {
|
||||
sess.dcx().fatal("Compilation failed, aborting rustdoc");
|
||||
}
|
||||
@ -787,7 +786,7 @@ fn main_args(
|
||||
gcx.enter(|tcx| {
|
||||
let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
|
||||
core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
|
||||
});
|
||||
})?;
|
||||
info!("finished with rustc");
|
||||
|
||||
if let Some(options) = scrape_examples_options {
|
||||
|
@ -72,9 +72,9 @@ enum WhichLine {
|
||||
/// and also //~^ ERROR message one for the preceding line, and
|
||||
/// //~| ERROR message two for that same line.
|
||||
///
|
||||
/// If cfg is not None (i.e., in an incremental test), then we look
|
||||
/// for `//[X]~` instead, where `X` is the current `cfg`.
|
||||
pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
|
||||
/// If revision is not None, then we look
|
||||
/// for `//[X]~` instead, where `X` is the current revision.
|
||||
pub fn load_errors(testfile: &Path, revision: Option<&str>) -> Vec<Error> {
|
||||
let rdr = BufReader::new(File::open(testfile).unwrap());
|
||||
|
||||
// `last_nonfollow_error` tracks the most recently seen
|
||||
@ -90,7 +90,7 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
|
||||
rdr.lines()
|
||||
.enumerate()
|
||||
.filter_map(|(line_num, line)| {
|
||||
parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), cfg).map(
|
||||
parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), revision).map(
|
||||
|(which, error)| {
|
||||
match which {
|
||||
FollowPrevious(_) => {}
|
||||
@ -108,24 +108,27 @@ fn parse_expected(
|
||||
last_nonfollow_error: Option<usize>,
|
||||
line_num: usize,
|
||||
line: &str,
|
||||
cfg: Option<&str>,
|
||||
test_revision: Option<&str>,
|
||||
) -> Option<(WhichLine, Error)> {
|
||||
// Matches comments like:
|
||||
// //~
|
||||
// //~|
|
||||
// //~^
|
||||
// //~^^^^^
|
||||
// //[cfg1]~
|
||||
// //[cfg1,cfg2]~^^
|
||||
// //[rev1]~
|
||||
// //[rev1,rev2]~^^
|
||||
static RE: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"//(?:\[(?P<cfgs>[\w,]+)])?~(?P<adjust>\||\^*)").unwrap());
|
||||
Lazy::new(|| Regex::new(r"//(?:\[(?P<revs>[\w,]+)])?~(?P<adjust>\||\^*)").unwrap());
|
||||
|
||||
let captures = RE.captures(line)?;
|
||||
|
||||
match (cfg, captures.name("cfgs")) {
|
||||
// Only error messages that contain our `cfg` between the square brackets apply to us.
|
||||
(Some(cfg), Some(filter)) if !filter.as_str().split(',').any(|s| s == cfg) => return None,
|
||||
(Some(_), Some(_)) => {}
|
||||
match (test_revision, captures.name("revs")) {
|
||||
// Only error messages that contain our revision between the square brackets apply to us.
|
||||
(Some(test_revision), Some(revision_filters)) => {
|
||||
if !revision_filters.as_str().split(',').any(|r| r == test_revision) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
(None, Some(_)) => panic!("Only tests with revisions should use `//[X]~`"),
|
||||
|
||||
|
@ -289,20 +289,20 @@ impl TestProps {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
|
||||
pub fn from_aux_file(&self, testfile: &Path, revision: Option<&str>, config: &Config) -> Self {
|
||||
let mut props = TestProps::new();
|
||||
|
||||
// copy over select properties to the aux build:
|
||||
props.incremental_dir = self.incremental_dir.clone();
|
||||
props.ignore_pass = true;
|
||||
props.load_from(testfile, cfg, config);
|
||||
props.load_from(testfile, revision, config);
|
||||
|
||||
props
|
||||
}
|
||||
|
||||
pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
|
||||
pub fn from_file(testfile: &Path, revision: Option<&str>, config: &Config) -> Self {
|
||||
let mut props = TestProps::new();
|
||||
props.load_from(testfile, cfg, config);
|
||||
props.load_from(testfile, revision, config);
|
||||
|
||||
match (props.pass_mode, props.fail_mode) {
|
||||
(None, None) if config.mode == Mode::Ui => props.fail_mode = Some(FailMode::Check),
|
||||
@ -315,9 +315,9 @@ impl TestProps {
|
||||
|
||||
/// Loads properties from `testfile` into `props`. If a property is
|
||||
/// tied to a particular revision `foo` (indicated by writing
|
||||
/// `//[foo]`), then the property is ignored unless `cfg` is
|
||||
/// `//@[foo]`), then the property is ignored unless `test_revision` is
|
||||
/// `Some("foo")`.
|
||||
fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
|
||||
fn load_from(&mut self, testfile: &Path, test_revision: Option<&str>, config: &Config) {
|
||||
let mut has_edition = false;
|
||||
if !testfile.is_dir() {
|
||||
let file = File::open(testfile).unwrap();
|
||||
@ -331,7 +331,7 @@ impl TestProps {
|
||||
testfile,
|
||||
file,
|
||||
&mut |HeaderLine { header_revision, directive: ln, .. }| {
|
||||
if header_revision.is_some() && header_revision != cfg {
|
||||
if header_revision.is_some() && header_revision != test_revision {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -455,7 +455,7 @@ impl TestProps {
|
||||
&mut self.check_test_line_numbers_match,
|
||||
);
|
||||
|
||||
self.update_pass_mode(ln, cfg, config);
|
||||
self.update_pass_mode(ln, test_revision, config);
|
||||
self.update_fail_mode(ln, config);
|
||||
|
||||
config.set_name_directive(ln, IGNORE_PASS, &mut self.ignore_pass);
|
||||
@ -645,30 +645,27 @@ impl TestProps {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a `(Option<line_config>, directive)` directive from a line if comment is present.
|
||||
/// Extract an `(Option<line_revision>, directive)` directive from a line if comment is present.
|
||||
///
|
||||
/// See [`HeaderLine`] for a diagram.
|
||||
pub fn line_directive<'line>(
|
||||
comment: &str,
|
||||
ln: &'line str,
|
||||
original_line: &'line str,
|
||||
) -> Option<(Option<&'line str>, &'line str)> {
|
||||
let ln = ln.trim_start();
|
||||
if ln.starts_with(comment) {
|
||||
let ln = ln[comment.len()..].trim_start();
|
||||
if ln.starts_with('[') {
|
||||
// A comment like `//[foo]` is specific to revision `foo`
|
||||
let Some(close_brace) = ln.find(']') else {
|
||||
panic!(
|
||||
"malformed condition directive: expected `{}[foo]`, found `{}`",
|
||||
comment, ln
|
||||
);
|
||||
};
|
||||
// Ignore lines that don't start with the comment prefix.
|
||||
let after_comment = original_line.trim_start().strip_prefix(comment)?.trim_start();
|
||||
|
||||
let lncfg = &ln[1..close_brace];
|
||||
Some((Some(lncfg), ln[(close_brace + 1)..].trim_start()))
|
||||
} else {
|
||||
Some((None, ln))
|
||||
}
|
||||
if let Some(after_open_bracket) = after_comment.strip_prefix('[') {
|
||||
// A comment like `//@[foo]` only applies to revision `foo`.
|
||||
let Some((line_revision, directive)) = after_open_bracket.split_once(']') else {
|
||||
panic!(
|
||||
"malformed condition directive: expected `{comment}[foo]`, found `{original_line}`"
|
||||
)
|
||||
};
|
||||
|
||||
Some((Some(line_revision), directive.trim_start()))
|
||||
} else {
|
||||
None
|
||||
Some((None, after_comment))
|
||||
}
|
||||
}
|
||||
|
||||
@ -790,16 +787,32 @@ const DIAGNOSTICS_DIRECTIVE_NAMES: &[&str] = &[
|
||||
"unset-rustc-env",
|
||||
];
|
||||
|
||||
/// Arguments passed to the callback in [`iter_header`].
|
||||
/// The broken-down contents of a line containing a test header directive,
|
||||
/// which [`iter_header`] passes to its callback function.
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// ```text
|
||||
/// //@ compile-flags: -O
|
||||
/// ^^^^^^^^^^^^^^^^^ directive
|
||||
/// ^^^^^^^^^^^^^^^^^^^^^ original_line
|
||||
///
|
||||
/// //@ [foo] compile-flags: -O
|
||||
/// ^^^ header_revision
|
||||
/// ^^^^^^^^^^^^^^^^^ directive
|
||||
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ original_line
|
||||
/// ```
|
||||
struct HeaderLine<'ln> {
|
||||
/// Contents of the square brackets preceding this header, if present.
|
||||
header_revision: Option<&'ln str>,
|
||||
line_number: usize,
|
||||
/// Raw line from the test file, including comment prefix and any revision.
|
||||
original_line: &'ln str,
|
||||
/// Remainder of the directive line, after the initial comment prefix
|
||||
/// (`//` or `//@` or `#`) and revision (if any) have been stripped.
|
||||
/// Some header directives start with a revision name in square brackets
|
||||
/// (e.g. `[foo]`), and only apply to that revision of the test.
|
||||
/// If present, this field contains the revision name (e.g. `foo`).
|
||||
header_revision: Option<&'ln str>,
|
||||
/// The main part of the header directive, after removing the comment prefix
|
||||
/// and the optional revision specifier.
|
||||
directive: &'ln str,
|
||||
line_number: usize,
|
||||
}
|
||||
|
||||
fn iter_header(
|
||||
@ -831,7 +844,7 @@ fn iter_header(
|
||||
];
|
||||
// Process the extra implied directives, with a dummy line number of 0.
|
||||
for directive in extra_directives {
|
||||
it(HeaderLine { header_revision: None, original_line: "", directive, line_number: 0 });
|
||||
it(HeaderLine { line_number: 0, original_line: "", header_revision: None, directive });
|
||||
}
|
||||
}
|
||||
|
||||
@ -865,7 +878,7 @@ fn iter_header(
|
||||
|
||||
// First try to accept `ui_test` style comments
|
||||
} else if let Some((header_revision, directive)) = line_directive(comment, ln) {
|
||||
it(HeaderLine { header_revision, original_line, directive, line_number });
|
||||
it(HeaderLine { line_number, original_line, header_revision, directive });
|
||||
} else if mode == Mode::Ui && suite == "ui" && !REVISION_MAGIC_COMMENT_RE.is_match(ln) {
|
||||
let Some((_, rest)) = line_directive("//", ln) else {
|
||||
continue;
|
||||
@ -1158,7 +1171,7 @@ pub fn make_test_description<R: Read>(
|
||||
name: test::TestName,
|
||||
path: &Path,
|
||||
src: R,
|
||||
cfg: Option<&str>,
|
||||
test_revision: Option<&str>,
|
||||
poisoned: &mut bool,
|
||||
) -> test::TestDesc {
|
||||
let mut ignore = false;
|
||||
@ -1174,7 +1187,7 @@ pub fn make_test_description<R: Read>(
|
||||
path,
|
||||
src,
|
||||
&mut |HeaderLine { header_revision, original_line, directive: ln, line_number }| {
|
||||
if header_revision.is_some() && header_revision != cfg {
|
||||
if header_revision.is_some() && header_revision != test_revision {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10,12 +10,19 @@ fn make_test_description<R: Read>(
|
||||
name: test::TestName,
|
||||
path: &Path,
|
||||
src: R,
|
||||
cfg: Option<&str>,
|
||||
revision: Option<&str>,
|
||||
) -> test::TestDesc {
|
||||
let cache = HeadersCache::load(config);
|
||||
let mut poisoned = false;
|
||||
let test =
|
||||
crate::header::make_test_description(config, &cache, name, path, src, cfg, &mut poisoned);
|
||||
let test = crate::header::make_test_description(
|
||||
config,
|
||||
&cache,
|
||||
name,
|
||||
path,
|
||||
src,
|
||||
revision,
|
||||
&mut poisoned,
|
||||
);
|
||||
if poisoned {
|
||||
panic!("poisoned!");
|
||||
}
|
||||
|
@ -745,7 +745,7 @@ fn make_test(
|
||||
let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
|
||||
vec![None]
|
||||
} else {
|
||||
early_props.revisions.iter().map(Some).collect()
|
||||
early_props.revisions.iter().map(|r| Some(r.as_str())).collect()
|
||||
};
|
||||
|
||||
revisions
|
||||
@ -753,20 +753,13 @@ fn make_test(
|
||||
.map(|revision| {
|
||||
let src_file =
|
||||
std::fs::File::open(&test_path).expect("open test file to parse ignores");
|
||||
let cfg = revision.map(|v| &**v);
|
||||
let test_name = crate::make_test_name(&config, testpaths, revision);
|
||||
let mut desc = make_test_description(
|
||||
&config, cache, test_name, &test_path, src_file, cfg, poisoned,
|
||||
&config, cache, test_name, &test_path, src_file, revision, poisoned,
|
||||
);
|
||||
// Ignore tests that already run and are up to date with respect to inputs.
|
||||
if !config.force_rerun {
|
||||
desc.ignore |= is_up_to_date(
|
||||
&config,
|
||||
testpaths,
|
||||
&early_props,
|
||||
revision.map(|s| s.as_str()),
|
||||
inputs,
|
||||
);
|
||||
desc.ignore |= is_up_to_date(&config, testpaths, &early_props, revision, inputs);
|
||||
}
|
||||
test::TestDescAndFn {
|
||||
desc,
|
||||
@ -879,7 +872,7 @@ impl Stamp {
|
||||
fn make_test_name(
|
||||
config: &Config,
|
||||
testpaths: &TestPaths,
|
||||
revision: Option<&String>,
|
||||
revision: Option<&str>,
|
||||
) -> test::TestName {
|
||||
// Print the name of the file, relative to the repository root.
|
||||
// `src_base` looks like `/path/to/rust/tests/ui`
|
||||
@ -907,11 +900,11 @@ fn make_test_name(
|
||||
fn make_test_closure(
|
||||
config: Arc<Config>,
|
||||
testpaths: &TestPaths,
|
||||
revision: Option<&String>,
|
||||
revision: Option<&str>,
|
||||
) -> test::TestFn {
|
||||
let config = config.clone();
|
||||
let testpaths = testpaths.clone();
|
||||
let revision = revision.cloned();
|
||||
let revision = revision.map(str::to_owned);
|
||||
test::DynTestFn(Box::new(move || {
|
||||
runtest::run(config, &testpaths, revision.as_deref());
|
||||
Ok(())
|
||||
|
@ -68,7 +68,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
||||
queries: &'tcx rustc_interface::Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
queries.global_ctxt().unwrap().enter(|tcx| {
|
||||
if tcx.sess.compile_status().is_err() {
|
||||
if tcx.sess.dcx().has_errors().is_some() {
|
||||
tcx.dcx().fatal("miri cannot be run on programs that fail compilation");
|
||||
}
|
||||
|
||||
|
@ -7,18 +7,17 @@ Number of file 0 mappings: 1
|
||||
- Code(Counter(0)) at (prev + 29, 1) to (start + 2, 2)
|
||||
|
||||
Function name: closure_macro::main
|
||||
Raw bytes (43): 0x[01, 01, 02, 01, 05, 05, 02, 07, 01, 21, 01, 01, 21, 02, 02, 09, 00, 0f, 05, 00, 12, 00, 13, 02, 00, 12, 00, 13, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 07, 03, 01, 00, 02]
|
||||
Raw bytes (38): 0x[01, 01, 02, 01, 05, 05, 02, 06, 01, 21, 01, 01, 21, 02, 02, 09, 00, 12, 02, 00, 0f, 00, 54, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 07, 03, 01, 00, 02]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 2
|
||||
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
|
||||
Number of file 0 mappings: 7
|
||||
Number of file 0 mappings: 6
|
||||
- Code(Counter(0)) at (prev + 33, 1) to (start + 1, 33)
|
||||
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15)
|
||||
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 18)
|
||||
= (c0 - c1)
|
||||
- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19)
|
||||
- Code(Expression(0, Sub)) at (prev + 0, 18) to (start + 0, 19)
|
||||
- Code(Expression(0, Sub)) at (prev + 0, 15) to (start + 0, 84)
|
||||
= (c0 - c1)
|
||||
- Code(Counter(1)) at (prev + 0, 84) to (start + 0, 85)
|
||||
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 2, 11)
|
||||
|
@ -15,18 +15,17 @@ Number of file 0 mappings: 1
|
||||
- Code(Counter(0)) at (prev + 35, 1) to (start + 0, 43)
|
||||
|
||||
Function name: closure_macro_async::test::{closure#0}
|
||||
Raw bytes (43): 0x[01, 01, 02, 01, 05, 05, 02, 07, 01, 23, 2b, 01, 21, 02, 02, 09, 00, 0f, 05, 00, 12, 00, 13, 02, 00, 12, 00, 13, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 07, 03, 01, 00, 02]
|
||||
Raw bytes (38): 0x[01, 01, 02, 01, 05, 05, 02, 06, 01, 23, 2b, 01, 21, 02, 02, 09, 00, 12, 02, 00, 0f, 00, 54, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 07, 03, 01, 00, 02]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 2
|
||||
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
|
||||
Number of file 0 mappings: 7
|
||||
Number of file 0 mappings: 6
|
||||
- Code(Counter(0)) at (prev + 35, 43) to (start + 1, 33)
|
||||
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15)
|
||||
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 18)
|
||||
= (c0 - c1)
|
||||
- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19)
|
||||
- Code(Expression(0, Sub)) at (prev + 0, 18) to (start + 0, 19)
|
||||
- Code(Expression(0, Sub)) at (prev + 0, 15) to (start + 0, 84)
|
||||
= (c0 - c1)
|
||||
- Code(Counter(1)) at (prev + 0, 84) to (start + 0, 85)
|
||||
- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 2, 11)
|
||||
|
18
tests/ui/associated-type-bounds/no-gat-position.rs
Normal file
18
tests/ui/associated-type-bounds/no-gat-position.rs
Normal file
@ -0,0 +1,18 @@
|
||||
#![feature(associated_type_bounds)]
|
||||
|
||||
// Test for <https://github.com/rust-lang/rust/issues/119857>.
|
||||
|
||||
pub trait Iter {
|
||||
type Item<'a>: 'a where Self: 'a;
|
||||
|
||||
fn next<'a>(&'a mut self) -> Option<Self::Item<'a, As1: Copy>>;
|
||||
//~^ ERROR associated type bindings are not allowed here
|
||||
}
|
||||
|
||||
impl Iter for () {
|
||||
type Item<'a> = &'a mut [()];
|
||||
|
||||
fn next<'a>(&'a mut self) -> Option<Self::Item<'a>> { None }
|
||||
}
|
||||
|
||||
fn main() {}
|
9
tests/ui/associated-type-bounds/no-gat-position.stderr
Normal file
9
tests/ui/associated-type-bounds/no-gat-position.stderr
Normal file
@ -0,0 +1,9 @@
|
||||
error[E0229]: associated type bindings are not allowed here
|
||||
--> $DIR/no-gat-position.rs:8:56
|
||||
|
|
||||
LL | fn next<'a>(&'a mut self) -> Option<Self::Item<'a, As1: Copy>>;
|
||||
| ^^^^^^^^^ associated type not allowed here
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0229`.
|
@ -21,5 +21,3 @@ LL | same_output(foo, rpit);
|
||||
|
||||
query stack during panic:
|
||||
end of query stack
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -12,5 +12,3 @@ LL | builder.state().on_entry(|_| {});
|
||||
|
||||
query stack during panic:
|
||||
end of query stack
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
#![feature(generic_const_exprs)]
|
||||
//~^ WARN the feature `generic_const_exprs` is incomplete
|
||||
#![feature(non_lifetime_binders)]
|
||||
//~^ WARN the feature `non_lifetime_binders` is incomplete
|
||||
|
||||
// Test for <https://github.com/rust-lang/rust/issues/115497>,
|
||||
// which originally relied on associated_type_bounds, but was
|
||||
// minimized away from that.
|
||||
|
||||
trait TraitA {
|
||||
type AsA;
|
||||
}
|
||||
trait TraitB {
|
||||
type AsB;
|
||||
}
|
||||
trait TraitC {}
|
||||
|
||||
fn foo<T>()
|
||||
where
|
||||
for<const N: u8 = { T::A }> T: TraitA<AsA = impl TraitB<AsB = impl TraitC>>,
|
||||
//~^ ERROR defaults for generic parameters are not allowed in `for<...>` binders
|
||||
//~| ERROR `impl Trait` is not allowed in bounds
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,34 @@
|
||||
warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/bad-suggestion-on-missing-assoc.rs:1:12
|
||||
|
|
||||
LL | #![feature(generic_const_exprs)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/bad-suggestion-on-missing-assoc.rs:3:12
|
||||
|
|
||||
LL | #![feature(non_lifetime_binders)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
|
||||
|
||||
error: defaults for generic parameters are not allowed in `for<...>` binders
|
||||
--> $DIR/bad-suggestion-on-missing-assoc.rs:20:9
|
||||
|
|
||||
LL | for<const N: u8 = { T::A }> T: TraitA<AsA = impl TraitB<AsB = impl TraitC>>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0562]: `impl Trait` is not allowed in bounds
|
||||
--> $DIR/bad-suggestion-on-missing-assoc.rs:20:49
|
||||
|
|
||||
LL | for<const N: u8 = { T::A }> T: TraitA<AsA = impl TraitB<AsB = impl TraitC>>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
|
||||
|
||||
error: aborting due to 2 previous errors; 2 warnings emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0562`.
|
@ -21,5 +21,3 @@ LL | query(get_rpit);
|
||||
|
||||
query stack during panic:
|
||||
end of query stack
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -423,6 +423,10 @@ changelog-branch = "master"
|
||||
|
||||
[shortcut]
|
||||
|
||||
[mentions."triagebot.toml"]
|
||||
message = "`triagebot.toml` has been modified, there may have been changes to the review queue."
|
||||
cc = ["@davidtwco", "@wesleywiser"]
|
||||
|
||||
[mentions."compiler/rustc_codegen_cranelift"]
|
||||
cc = ["@bjorn3"]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user