Skip to content

Commit

Permalink
Auto merge of rust-lang#126834 - bjorn3:interface_refactor, r=michael…
Browse files Browse the repository at this point in the history
…woerister

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.
  • Loading branch information
bors committed Jun 25, 2024
2 parents bda221a + 8d1f5b3 commit c2d2bb3
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 184 deletions.
29 changes: 16 additions & 13 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}

Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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")
}
}
}
Expand Down
21 changes: 9 additions & 12 deletions compiler/rustc_interface/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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() {
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<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
};
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
145 changes: 117 additions & 28 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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};
Expand All @@ -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<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()
})
.map_err(|parse_error| parse_error.emit())?;

if sess.opts.unstable_opts.input_stats {
eprintln!("Lines of code: {}", sess.source_map().count_lines());
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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)
})
}

Expand Down Expand Up @@ -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);
Expand All @@ -952,7 +1041,7 @@ pub fn start_codegen<'tcx>(
}
}

codegen
Ok(codegen)
}

fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit {
Expand Down
Loading

0 comments on commit c2d2bb3

Please sign in to comment.