diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index f8c68791564..bbe9741bf44 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -30,7 +30,7 @@ use rustc_errors::{ }; use rustc_feature::find_gated_cfg; use rustc_interface::util::{self, get_codegen_backend}; -use rustc_interface::{interface, Queries}; +use rustc_interface::{interface, passes, Queries}; use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; @@ -367,18 +367,17 @@ fn run_compiler( 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 { + if print_crate_info(codegen_backend, sess, has_input) == Compilation::Stop { return early_exit(); } if !has_input { - early_dcx.early_fatal("no input filename given"); // this is fatal + #[allow(rustc::diagnostic_outside_of_impl)] + sess.dcx().fatal("no input filename given"); // this is fatal } if !sess.opts.unstable_opts.ls.is_empty() { - list_metadata(&early_dcx, sess, &*codegen_backend.metadata_loader()); + list_metadata(sess, &*codegen_backend.metadata_loader()); return early_exit(); } @@ -399,7 +398,9 @@ fn run_compiler( Ok(()) })?; - queries.write_dep_info()?; + queries.global_ctxt()?.enter(|tcx| { + passes::write_dep_info(tcx); + }); } else { let krate = queries.parse()?; pretty::print( @@ -427,7 +428,9 @@ fn run_compiler( return early_exit(); } - queries.write_dep_info()?; + queries.global_ctxt()?.enter(|tcx| { + passes::write_dep_info(tcx); + }); if sess.opts.output_types.contains_key(&OutputType::DepInfo) && sess.opts.output_types.len() == 1 @@ -670,7 +673,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { } } -fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dyn MetadataLoader) { +fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { match sess.io.input { Input::File(ref ifile) => { let path = &(*ifile); @@ -687,13 +690,13 @@ fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dy safe_println!("{}", String::from_utf8(v).unwrap()); } Input::Str { .. } => { - early_dcx.early_fatal("cannot list metadata for stdin"); + #[allow(rustc::diagnostic_outside_of_impl)] + sess.dcx().fatal("cannot list metadata for stdin"); } } } fn print_crate_info( - early_dcx: &EarlyDiagCtxt, codegen_backend: &dyn CodegenBackend, sess: &Session, parse_attrs: bool, @@ -877,8 +880,8 @@ fn print_crate_info( .expect("unknown Apple target OS"); println_info!("deployment_target={}", format!("{major}.{minor}")) } else { - early_dcx - .early_fatal("only Apple targets currently support deployment version info") + #[allow(rustc::diagnostic_outside_of_impl)] + sess.dcx().fatal("only Apple targets currently support deployment version info") } } } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 41c8b941717..dba20e4a335 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -495,9 +495,8 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se 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); + compiler.sess.finish_diagnostics(&config.registry); }); let res = f(&compiler); @@ -506,16 +505,14 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se // 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`. - // 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(); - } + // If error diagnostics have been emitted, we can't return an + // error directly, because the return type of this function + // is `R`, not `Result`. 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. + compiler.sess.dcx().abort_if_errors(); res }; diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 0c3d4e19ef8..38f64ebb04e 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -8,7 +8,7 @@ mod callbacks; mod errors; pub mod interface; -mod passes; +pub mod passes; mod proc_macro_decls; mod queries; pub mod util; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index f881d53858a..2951f50b1f5 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -7,16 +7,16 @@ use rustc_ast::{self as ast, visit}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::parallel; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{Lrc, OnceLock, WorkerLocal}; -use rustc_errors::PResult; +use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, Lrc, OnceLock, WorkerLocal}; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; -use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; +use rustc_hir::def_id::{StableCrateId, StableCrateIdMap, LOCAL_CRATE}; +use rustc_hir::definitions::Definitions; +use rustc_incremental::setup_dep_graph; use rustc_lint::{unerased_lint_store, BufferedEarlyLint, EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; -use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; use rustc_parse::{ @@ -28,6 +28,7 @@ use rustc_session::code_stats::VTableSizeInfo; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; use rustc_session::output::filename_for_input; +use rustc_session::output::{collect_crate_types, find_crate_name}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Symbol}; @@ -39,20 +40,22 @@ use std::any::Any; use std::ffi::OsString; use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock}; use std::{env, fs, iter}; use tracing::{info, instrument}; -pub fn parse<'a>(sess: &'a Session) -> PResult<'a, ast::Crate> { - let krate = sess.time("parse_crate", || { - let mut parser = unwrap_or_emit_fatal(match &sess.io.input { - Input::File(file) => new_parser_from_file(&sess.psess, file, None), - Input::Str { input, name } => { - new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) - } - }); - parser.parse_crate_mod() - })?; +pub(crate) fn parse<'a>(sess: &'a Session) -> Result { + let krate = sess + .time("parse_crate", || { + let mut parser = unwrap_or_emit_fatal(match &sess.io.input { + Input::File(file) => new_parser_from_file(&sess.psess, file, None), + Input::Str { input, name } => { + new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) + } + }); + parser.parse_crate_mod() + }) + .map_err(|parse_error| parse_error.emit())?; if sess.opts.unstable_opts.input_stats { eprintln!("Lines of code: {}", sess.source_map().count_lines()); @@ -559,7 +562,7 @@ fn resolver_for_lowering_raw<'tcx>( (tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, Lrc::new(krate)))), resolutions) } -pub(crate) fn write_dep_info(tcx: TyCtxt<'_>) { +pub fn write_dep_info(tcx: TyCtxt<'_>) { // Make sure name resolution and macro expansion is run for // the side-effect of providing a complete set of all // accessed files and env vars. @@ -640,22 +643,48 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { *providers }); -pub fn create_global_ctxt<'tcx>( +pub(crate) fn create_global_ctxt<'tcx>( compiler: &'tcx Compiler, - crate_types: Vec, - stable_crate_id: StableCrateId, - dep_graph: DepGraph, - untracked: Untracked, + mut krate: rustc_ast::Crate, gcx_cell: &'tcx OnceLock>, arena: &'tcx WorkerLocal>, hir_arena: &'tcx WorkerLocal>, -) -> &'tcx GlobalCtxt<'tcx> { +) -> Result<&'tcx GlobalCtxt<'tcx>> { + let sess = &compiler.sess; + + rustc_builtin_macros::cmdline_attrs::inject( + &mut krate, + &sess.psess, + &sess.opts.unstable_opts.crate_attr, + ); + + let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs); + + // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches. + let crate_name = find_crate_name(sess, &pre_configured_attrs); + let crate_types = collect_crate_types(sess, &pre_configured_attrs); + let stable_crate_id = StableCrateId::new( + crate_name, + crate_types.contains(&CrateType::Executable), + sess.opts.cg.metadata.clone(), + sess.cfg_version, + ); + let outputs = util::build_output_filenames(&pre_configured_attrs, sess); + let dep_graph = setup_dep_graph(sess)?; + + let cstore = + FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); + let definitions = FreezeLock::new(Definitions::new(stable_crate_id)); + + let stable_crate_ids = FreezeLock::new(StableCrateIdMap::default()); + let untracked = + Untracked { cstore, source_span: AppendOnlyIndexVec::new(), definitions, stable_crate_ids }; + // We're constructing the HIR here; we don't care what we will // read, since we haven't even constructed the *input* to // incr. comp. yet. dep_graph.assert_ignored(); - let sess = &compiler.sess; let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess); let codegen_backend = &compiler.codegen_backend; @@ -669,7 +698,7 @@ pub fn create_global_ctxt<'tcx>( let incremental = dep_graph.is_fully_enabled(); sess.time("setup_global_ctxt", || { - gcx_cell.get_or_init(move || { + let qcx = gcx_cell.get_or_init(move || { TyCtxt::create_global_ctxt( sess, crate_types, @@ -688,7 +717,23 @@ pub fn create_global_ctxt<'tcx>( providers.hooks, compiler.current_gcx.clone(), ) - }) + }); + + qcx.enter(|tcx| { + let feed = tcx.create_crate_num(stable_crate_id).unwrap(); + assert_eq!(feed.key(), LOCAL_CRATE); + feed.crate_name(crate_name); + + let feed = tcx.feed_unit_query(); + feed.features_query(tcx.arena.alloc(rustc_expand::config::features( + sess, + &pre_configured_attrs, + crate_name, + ))); + feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); + feed.output_filenames(Arc::new(outputs)); + }); + Ok(qcx) }) } @@ -924,12 +969,56 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { Ok(()) } +/// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used +/// to write UI tests that actually test that compilation succeeds without reporting +/// an error. +fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { + let Some((def_id, _)) = tcx.entry_fn(()) else { return }; + for attr in tcx.get_attrs(def_id, sym::rustc_error) { + match attr.meta_item_list() { + // Check if there is a `#[rustc_error(delayed_bug_from_inside_query)]`. + Some(list) + if list.iter().any(|list_item| { + matches!( + list_item.ident().map(|i| i.name), + Some(sym::delayed_bug_from_inside_query) + ) + }) => + { + tcx.ensure().trigger_delayed_bug(def_id); + } + + // Bare `#[rustc_error]`. + None => { + tcx.dcx().emit_fatal(errors::RustcErrorFatal { span: tcx.def_span(def_id) }); + } + + // Some other attribute. + Some(_) => { + tcx.dcx().emit_warn(errors::RustcErrorUnexpectedAnnotation { + span: tcx.def_span(def_id), + }); + } + } + } +} + /// Runs the codegen backend, after which the AST and analysis can /// be discarded. -pub fn start_codegen<'tcx>( +pub(crate) fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, -) -> Box { +) -> Result> { + // 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) = tcx.sess.dcx().has_errors_or_delayed_bugs() { + return Err(guar); + } + + // Hook for UI tests. + check_for_rustc_errors_attr(tcx); + info!("Pre-codegen\n{:?}", tcx.debug_stats()); let (metadata, need_metadata_module) = rustc_metadata::fs::encode_and_write_metadata(tcx); @@ -952,7 +1041,7 @@ pub fn start_codegen<'tcx>( } } - codegen + Ok(codegen) } fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 1b9165342d4..cfd4304e893 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -1,26 +1,20 @@ -use crate::errors::{FailedWritingFile, RustcErrorFatal, RustcErrorUnexpectedAnnotation}; +use crate::errors::FailedWritingFile; use crate::interface::{Compiler, Result}; -use crate::{errors, passes, util}; +use crate::{errors, passes}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, OnceLock, WorkerLocal}; -use rustc_hir::def_id::{StableCrateId, StableCrateIdMap, LOCAL_CRATE}; -use rustc_hir::definitions::Definitions; -use rustc_incremental::setup_dep_graph; -use rustc_metadata::creader::CStore; +use rustc_data_structures::sync::{OnceLock, WorkerLocal}; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::{GlobalCtxt, TyCtxt}; use rustc_serialize::opaque::FileEncodeResult; -use rustc_session::config::{self, CrateType, OutputFilenames, OutputType}; -use rustc_session::cstore::Untracked; -use rustc_session::output::{collect_crate_types, find_crate_name}; +use rustc_session::config::{self, OutputFilenames, OutputType}; use rustc_session::Session; -use rustc_span::symbol::sym; use std::any::Any; use std::cell::{RefCell, RefMut}; use std::sync::Arc; @@ -106,133 +100,26 @@ impl<'tcx> Queries<'tcx> { } pub fn parse(&self) -> Result> { - self.parse.compute(|| { - passes::parse(&self.compiler.sess).map_err(|parse_error| parse_error.emit()) - }) + self.parse.compute(|| passes::parse(&self.compiler.sess)) } pub fn global_ctxt(&'tcx self) -> Result>> { self.gcx.compute(|| { - let sess = &self.compiler.sess; + let krate = self.parse()?.steal(); - let mut krate = self.parse()?.steal(); - - rustc_builtin_macros::cmdline_attrs::inject( - &mut krate, - &sess.psess, - &sess.opts.unstable_opts.crate_attr, - ); - - let pre_configured_attrs = - rustc_expand::config::pre_configure_attrs(sess, &krate.attrs); - - // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches. - let crate_name = find_crate_name(sess, &pre_configured_attrs); - let crate_types = collect_crate_types(sess, &pre_configured_attrs); - let stable_crate_id = StableCrateId::new( - crate_name, - crate_types.contains(&CrateType::Executable), - sess.opts.cg.metadata.clone(), - sess.cfg_version, - ); - let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess)?; - - let cstore = FreezeLock::new(Box::new(CStore::new( - self.compiler.codegen_backend.metadata_loader(), - )) as _); - let definitions = FreezeLock::new(Definitions::new(stable_crate_id)); - - let stable_crate_ids = FreezeLock::new(StableCrateIdMap::default()); - let untracked = Untracked { - cstore, - source_span: AppendOnlyIndexVec::new(), - definitions, - stable_crate_ids, - }; - - let qcx = passes::create_global_ctxt( + passes::create_global_ctxt( self.compiler, - crate_types, - stable_crate_id, - dep_graph, - untracked, + krate, &self.gcx_cell, &self.arena, &self.hir_arena, - ); - - qcx.enter(|tcx| { - let feed = tcx.create_crate_num(stable_crate_id).unwrap(); - assert_eq!(feed.key(), LOCAL_CRATE); - feed.crate_name(crate_name); - - let feed = tcx.feed_unit_query(); - feed.features_query(tcx.arena.alloc(rustc_expand::config::features( - sess, - &pre_configured_attrs, - crate_name, - ))); - feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); - feed.output_filenames(Arc::new(outputs)); - }); - Ok(qcx) + ) }) } - pub fn write_dep_info(&'tcx self) -> Result<()> { - self.global_ctxt()?.enter(|tcx| { - passes::write_dep_info(tcx); - }); - Ok(()) - } - - /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used - /// to write UI tests that actually test that compilation succeeds without reporting - /// an error. - fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { - let Some((def_id, _)) = tcx.entry_fn(()) else { return }; - for attr in tcx.get_attrs(def_id, sym::rustc_error) { - match attr.meta_item_list() { - // Check if there is a `#[rustc_error(delayed_bug_from_inside_query)]`. - Some(list) - if list.iter().any(|list_item| { - matches!( - list_item.ident().map(|i| i.name), - Some(sym::delayed_bug_from_inside_query) - ) - }) => - { - tcx.ensure().trigger_delayed_bug(def_id); - } - - // Bare `#[rustc_error]`. - None => { - tcx.dcx().emit_fatal(RustcErrorFatal { span: tcx.def_span(def_id) }); - } - - // Some other attribute. - Some(_) => { - tcx.dcx() - .emit_warn(RustcErrorUnexpectedAnnotation { span: tcx.def_span(def_id) }); - } - } - } - } - pub fn codegen_and_build_linker(&'tcx self) -> Result { self.global_ctxt()?.enter(|tcx| { - // 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); - - let ongoing_codegen = passes::start_codegen(&*self.compiler.codegen_backend, tcx); + let ongoing_codegen = passes::start_codegen(&*self.compiler.codegen_backend, tcx)?; Ok(Linker { dep_graph: tcx.dep_graph.clone(), diff --git a/tests/run-make/jobserver-error/cannot_open_fd.stderr b/tests/run-make/jobserver-error/cannot_open_fd.stderr index 7c421846535..9ac4c1c58f7 100644 --- a/tests/run-make/jobserver-error/cannot_open_fd.stderr +++ b/tests/run-make/jobserver-error/cannot_open_fd.stderr @@ -4,3 +4,5 @@ warning: failed to connect to jobserver from environment variable `MAKEFLAGS="-- error: no input filename given +error: aborting due to 1 previous error + diff --git a/tests/run-make/no-input-file/rmake.rs b/tests/run-make/no-input-file/rmake.rs index fc558b22fb4..d76a8f9e7c1 100644 --- a/tests/run-make/no-input-file/rmake.rs +++ b/tests/run-make/no-input-file/rmake.rs @@ -1,9 +1,9 @@ use run_make_support::rustc; fn main() { - rustc() - .print("crate-name") - .run_fail() - .assert_exit_code(1) - .assert_stderr_equals("error: no input filename given"); + rustc().print("crate-name").run_fail().assert_exit_code(1).assert_stderr_equals( + "error: no input filename given + +error: aborting due to 1 previous error", + ); } diff --git a/tests/ui/deployment-target/invalid-target.stderr b/tests/ui/deployment-target/invalid-target.stderr index eb4ac131c40..0ab548f339b 100644 --- a/tests/ui/deployment-target/invalid-target.stderr +++ b/tests/ui/deployment-target/invalid-target.stderr @@ -1,2 +1,4 @@ error: only Apple targets currently support deployment version info +error: aborting due to 1 previous error +