Auto merge of #126834 - bjorn3:interface_refactor, r=michaelwoerister

Various refactorings to rustc_interface

This should make it easier to move the driver interface away from queries in the future. Many custom drivers call queries like `queries.global_ctxt()` before they are supposed to be called, breaking some things like certain `--print` and `-Zunpretty` options, `-Zparse-only` and emitting the dep info at the wrong point in time. They are also not actually necessary at all. Passing around the query output manually would avoid recomputation too and would be just as easy. Removing driver queries would also reduce the amount of global mutable state of the compiler. I'm not removing driver queries in this PR to avoid breaking the aforementioned custom drivers.
This commit is contained in:
bors 2024-06-25 09:35:53 +00:00
commit c2d2bb38c9
8 changed files with 163 additions and 183 deletions

View File

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

View File

@ -495,9 +495,8 @@ pub fn run_compiler<R: Send>(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<R: Send>(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<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() {
// If error diagnostics have been emitted, 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.
compiler.sess.dcx().abort_if_errors();
}
res
};

View File

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

View File

@ -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,12 +40,13 @@ 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", || {
pub(crate) fn parse<'a>(sess: &'a Session) -> Result<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 } => {
@ -52,7 +54,8 @@ pub fn parse<'a>(sess: &'a Session) -> PResult<'a, ast::Crate> {
}
});
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<Providers> = LazyLock::new(|| {
*providers
});
pub fn create_global_ctxt<'tcx>(
pub(crate) fn create_global_ctxt<'tcx>(
compiler: &'tcx Compiler,
crate_types: Vec<CrateType>,
stable_crate_id: StableCrateId,
dep_graph: DepGraph,
untracked: Untracked,
mut krate: rustc_ast::Crate,
gcx_cell: &'tcx OnceLock<GlobalCtxt<'tcx>>,
arena: &'tcx WorkerLocal<Arena<'tcx>>,
hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>,
) -> &'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<dyn Any> {
) -> Result<Box<dyn Any>> {
// 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 {

View File

@ -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<QueryResult<'_, ast::Crate>> {
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<QueryResult<'_, &'tcx GlobalCtxt<'tcx>>> {
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<Linker> {
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(),

View File

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

View File

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

View File

@ -1,2 +1,4 @@
error: only Apple targets currently support deployment version info
error: aborting due to 1 previous error