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:
bors 2024-02-22 00:04:07 +00:00
commit d8b00690ec
50 changed files with 452 additions and 449 deletions

View File

@ -4048,7 +4048,6 @@ dependencies = [
name = "rustc_interface"
version = "0.0.0"
dependencies = [
"libloading",
"rustc-rayon",
"rustc-rayon-core",
"rustc_ast",

View File

@ -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();
}
}

View File

@ -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");
}
}

View File

@ -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

View File

@ -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 {});
}
}

View File

@ -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));

View File

@ -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;
}

View File

@ -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, _) => {

View File

@ -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.

View File

@ -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;
}

View File

@ -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)

View File

@ -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" }

View File

@ -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
})
},
)

View File

@ -1,5 +1,4 @@
#![feature(decl_macro)]
#![feature(error_iter)]
#![feature(generic_nonzero)]
#![feature(lazy_cell)]
#![feature(let_chains)]

View File

@ -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 });
}
}

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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)
}

View File

@ -535,6 +535,7 @@ pub struct StableCrateIdCollision {
pub struct DlError {
#[primary_span]
pub span: Span,
pub path: String,
pub err: String,
}

View File

@ -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};

View File

@ -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;

View File

@ -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>, ()>;

View File

@ -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");
}
}
}

View File

@ -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
}

View File

@ -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);

View File

@ -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);

View File

@ -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")
}

View File

@ -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();
}
}

View File

@ -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.

View File

@ -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>(

View File

@ -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

View File

@ -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>,

View File

@ -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();
}

View File

@ -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 {

View File

@ -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]~`"),

View File

@ -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;
}

View File

@ -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!");
}

View File

@ -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(())

View File

@ -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");
}

View File

@ -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)

View File

@ -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)

View 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() {}

View 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`.

View File

@ -21,5 +21,3 @@ LL | same_output(foo, rpit);
query stack during panic:
end of query stack
error: aborting due to 2 previous errors

View File

@ -12,5 +12,3 @@ LL | builder.state().on_entry(|_| {});
query stack during panic:
end of query stack
error: aborting due to 1 previous error

View File

@ -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() {}

View File

@ -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`.

View File

@ -21,5 +21,3 @@ LL | query(get_rpit);
query stack during panic:
end of query stack
error: aborting due to 2 previous errors

View File

@ -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"]