about summary refs log tree commit diff
path: root/compiler/rustc_interface/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-08-30 15:57:57 +0000
committerbors <bors@rust-lang.org>2020-08-30 15:57:57 +0000
commit85fbf49ce0e2274d0acf798f6e703747674feec3 (patch)
tree158a05eb3f204a8e72939b58427d0c2787a4eade /compiler/rustc_interface/src
parentdb534b3ac286cf45688c3bbae6aa6e77439e52d2 (diff)
parent9e5f7d5631b8f4009ac1c693e585d4b7108d4275 (diff)
downloadrust-85fbf49ce0e2274d0acf798f6e703747674feec3.tar.gz
rust-85fbf49ce0e2274d0acf798f6e703747674feec3.zip
Auto merge of #74862 - mark-i-m:mv-compiler, r=petrochenkov
Move almost all compiler crates to compiler/

This PR implements https://github.com/rust-lang/compiler-team/issues/336 and moves all `rustc_*` crates from `src` to the new `compiler` directory.

`librustc_foo` directories are renamed to `rustc_foo`.
`src` directories are introduced inside `rustc_*` directories to mirror the scheme already use for `library` crates.
Diffstat (limited to 'compiler/rustc_interface/src')
-rw-r--r--compiler/rustc_interface/src/callbacks.rs61
-rw-r--r--compiler/rustc_interface/src/interface.rs209
-rw-r--r--compiler/rustc_interface/src/lib.rs21
-rw-r--r--compiler/rustc_interface/src/passes.rs1004
-rw-r--r--compiler/rustc_interface/src/proc_macro_decls.rs40
-rw-r--r--compiler/rustc_interface/src/queries.rs397
-rw-r--r--compiler/rustc_interface/src/tests.rs599
-rw-r--r--compiler/rustc_interface/src/util.rs770
8 files changed, 3101 insertions, 0 deletions
diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs
new file mode 100644
index 00000000000..7fa1a3eb0f5
--- /dev/null
+++ b/compiler/rustc_interface/src/callbacks.rs
@@ -0,0 +1,61 @@
+//! Throughout the compiler tree, there are several places which want to have
+//! access to state or queries while being inside crates that are dependencies
+//! of librustc_middle. To facilitate this, we have the
+//! `rustc_data_structures::AtomicRef` type, which allows us to setup a global
+//! static which can then be set in this file at program startup.
+//!
+//! See `SPAN_DEBUG` for an example of how to set things up.
+//!
+//! The functions in this file should fall back to the default set in their
+//! origin crate when the `TyCtxt` is not present in TLS.
+
+use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS};
+use rustc_middle::ty::tls;
+use std::fmt;
+
+/// This is a callback from librustc_ast as it cannot access the implicit state
+/// in librustc_middle otherwise.
+fn span_debug(span: rustc_span::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    tls::with_opt(|tcx| {
+        if let Some(tcx) = tcx {
+            rustc_span::debug_with_source_map(span, f, tcx.sess.source_map())
+        } else {
+            rustc_span::default_span_debug(span, f)
+        }
+    })
+}
+
+/// This is a callback from librustc_ast as it cannot access the implicit state
+/// in librustc_middle otherwise. It is used to when diagnostic messages are
+/// emitted and stores them in the current query, if there is one.
+fn track_diagnostic(diagnostic: &Diagnostic) {
+    tls::with_context_opt(|icx| {
+        if let Some(icx) = icx {
+            if let Some(ref diagnostics) = icx.diagnostics {
+                let mut diagnostics = diagnostics.lock();
+                diagnostics.extend(Some(diagnostic.clone()));
+            }
+        }
+    })
+}
+
+/// This is a callback from librustc_hir as it cannot access the implicit state
+/// in librustc_middle otherwise.
+fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    write!(f, "DefId({}:{}", def_id.krate, def_id.index.index())?;
+    tls::with_opt(|opt_tcx| {
+        if let Some(tcx) = opt_tcx {
+            write!(f, " ~ {}", tcx.def_path_debug_str(def_id))?;
+        }
+        Ok(())
+    })?;
+    write!(f, ")")
+}
+
+/// Sets up the callbacks in prior crates which we want to refer to the
+/// TyCtxt in.
+pub fn setup_callbacks() {
+    rustc_span::SPAN_DEBUG.swap(&(span_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
+    rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
+    TRACK_DIAGNOSTICS.swap(&(track_diagnostic as fn(&_)));
+}
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
new file mode 100644
index 00000000000..4d84462c42b
--- /dev/null
+++ b/compiler/rustc_interface/src/interface.rs
@@ -0,0 +1,209 @@
+pub use crate::passes::BoxedResolver;
+use crate::util;
+
+use rustc_ast::token;
+use rustc_ast::{self as ast, MetaItemKind};
+use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::OnDrop;
+use rustc_errors::registry::Registry;
+use rustc_errors::ErrorReported;
+use rustc_lint::LintStore;
+use rustc_middle::ty;
+use rustc_parse::new_parser_from_source_str;
+use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames};
+use rustc_session::early_error;
+use rustc_session::lint;
+use rustc_session::parse::{CrateConfig, ParseSess};
+use rustc_session::{DiagnosticOutput, Session};
+use rustc_span::source_map::{FileLoader, FileName};
+use std::path::PathBuf;
+use std::result;
+use std::sync::{Arc, Mutex};
+
+pub type Result<T> = result::Result<T, ErrorReported>;
+
+/// Represents a compiler session.
+/// Can be used to run `rustc_interface` queries.
+/// Created by passing `Config` to `run_compiler`.
+pub struct Compiler {
+    pub(crate) sess: Lrc<Session>,
+    codegen_backend: Lrc<Box<dyn CodegenBackend>>,
+    pub(crate) input: Input,
+    pub(crate) input_path: Option<PathBuf>,
+    pub(crate) output_dir: Option<PathBuf>,
+    pub(crate) output_file: Option<PathBuf>,
+    pub(crate) crate_name: Option<String>,
+    pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
+    pub(crate) override_queries:
+        Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::Providers)>,
+}
+
+impl Compiler {
+    pub fn session(&self) -> &Lrc<Session> {
+        &self.sess
+    }
+    pub fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> {
+        &self.codegen_backend
+    }
+    pub fn input(&self) -> &Input {
+        &self.input
+    }
+    pub fn output_dir(&self) -> &Option<PathBuf> {
+        &self.output_dir
+    }
+    pub fn output_file(&self) -> &Option<PathBuf> {
+        &self.output_file
+    }
+    pub fn build_output_filenames(
+        &self,
+        sess: &Session,
+        attrs: &[ast::Attribute],
+    ) -> OutputFilenames {
+        util::build_output_filenames(
+            &self.input,
+            &self.output_dir,
+            &self.output_file,
+            &attrs,
+            &sess,
+        )
+    }
+}
+
+/// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
+pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> {
+    rustc_span::with_default_session_globals(move || {
+        let cfg = cfgspecs
+            .into_iter()
+            .map(|s| {
+                let sess = ParseSess::with_silent_emitter();
+                let filename = FileName::cfg_spec_source_code(&s);
+                let mut parser = new_parser_from_source_str(&sess, filename, s.to_string());
+
+                macro_rules! error {
+                    ($reason: expr) => {
+                        early_error(
+                            ErrorOutputType::default(),
+                            &format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s),
+                        );
+                    };
+                }
+
+                match &mut parser.parse_meta_item() {
+                    Ok(meta_item) if parser.token == token::Eof => {
+                        if meta_item.path.segments.len() != 1 {
+                            error!("argument key must be an identifier");
+                        }
+                        match &meta_item.kind {
+                            MetaItemKind::List(..) => {
+                                error!(r#"expected `key` or `key="value"`"#);
+                            }
+                            MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
+                                error!("argument value must be a string");
+                            }
+                            MetaItemKind::NameValue(..) | MetaItemKind::Word => {
+                                let ident = meta_item.ident().expect("multi-segment cfg key");
+                                return (ident.name, meta_item.value_str());
+                            }
+                        }
+                    }
+                    Ok(..) => {}
+                    Err(err) => err.cancel(),
+                }
+
+                error!(r#"expected `key` or `key="value"`"#);
+            })
+            .collect::<CrateConfig>();
+        cfg.into_iter().map(|(a, b)| (a.to_string(), b.map(|b| b.to_string()))).collect()
+    })
+}
+
+/// The compiler configuration
+pub struct Config {
+    /// Command line options
+    pub opts: config::Options,
+
+    /// cfg! configuration in addition to the default ones
+    pub crate_cfg: FxHashSet<(String, Option<String>)>,
+
+    pub input: Input,
+    pub input_path: Option<PathBuf>,
+    pub output_dir: Option<PathBuf>,
+    pub output_file: Option<PathBuf>,
+    pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
+    pub diagnostic_output: DiagnosticOutput,
+
+    /// Set to capture stderr output during compiler execution
+    pub stderr: Option<Arc<Mutex<Vec<u8>>>>,
+
+    pub crate_name: Option<String>,
+    pub lint_caps: FxHashMap<lint::LintId, lint::Level>,
+
+    /// This is a callback from the driver that is called when we're registering lints;
+    /// it is called during plugin registration when we have the LintStore in a non-shared state.
+    ///
+    /// Note that if you find a Some here you probably want to call that function in the new
+    /// function being registered.
+    pub register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
+
+    /// This is a callback from the driver that is called just after we have populated
+    /// the list of queries.
+    ///
+    /// The second parameter is local providers and the third parameter is external providers.
+    pub override_queries:
+        Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::Providers)>,
+
+    /// Registry of diagnostics codes.
+    pub registry: Registry,
+}
+
+pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R) -> R {
+    let registry = &config.registry;
+    let (sess, codegen_backend) = util::create_session(
+        config.opts,
+        config.crate_cfg,
+        config.diagnostic_output,
+        config.file_loader,
+        config.input_path.clone(),
+        config.lint_caps,
+        registry.clone(),
+    );
+
+    let compiler = Compiler {
+        sess,
+        codegen_backend,
+        input: config.input,
+        input_path: config.input_path,
+        output_dir: config.output_dir,
+        output_file: config.output_file,
+        crate_name: config.crate_name,
+        register_lints: config.register_lints,
+        override_queries: config.override_queries,
+    };
+
+    rustc_span::with_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
+        let r = {
+            let _sess_abort_error = OnDrop(|| {
+                compiler.sess.finish_diagnostics(registry);
+            });
+
+            f(&compiler)
+        };
+
+        let prof = compiler.sess.prof.clone();
+        prof.generic_activity("drop_compiler").run(move || drop(compiler));
+        r
+    })
+}
+
+pub fn run_compiler<R: Send>(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
+    tracing::trace!("run_compiler");
+    let stderr = config.stderr.take();
+    util::setup_callbacks_and_run_in_thread_pool_with_globals(
+        config.opts.edition,
+        config.opts.debugging_opts.threads,
+        &stderr,
+        || create_compiler_and_run(config, f),
+    )
+}
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
new file mode 100644
index 00000000000..fe40c615f79
--- /dev/null
+++ b/compiler/rustc_interface/src/lib.rs
@@ -0,0 +1,21 @@
+#![feature(bool_to_option)]
+#![feature(box_syntax)]
+#![feature(set_stdio)]
+#![feature(nll)]
+#![feature(generator_trait)]
+#![feature(generators)]
+#![recursion_limit = "256"]
+
+mod callbacks;
+pub mod interface;
+mod passes;
+mod proc_macro_decls;
+mod queries;
+pub mod util;
+
+pub use interface::{run_compiler, Config};
+pub use passes::{DEFAULT_EXTERN_QUERY_PROVIDERS, DEFAULT_QUERY_PROVIDERS};
+pub use queries::Queries;
+
+#[cfg(test)]
+mod tests;
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
new file mode 100644
index 00000000000..403aea8b304
--- /dev/null
+++ b/compiler/rustc_interface/src/passes.rs
@@ -0,0 +1,1004 @@
+use crate::interface::{Compiler, Result};
+use crate::proc_macro_decls;
+use crate::util;
+
+use once_cell::sync::Lazy;
+use rustc_ast::mut_visit::MutVisitor;
+use rustc_ast::{self as ast, visit};
+use rustc_codegen_ssa::back::link::emit_metadata;
+use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal};
+use rustc_data_structures::temp_dir::MaybeTempDir;
+use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel};
+use rustc_errors::{ErrorReported, PResult};
+use rustc_expand::base::ExtCtxt;
+use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
+use rustc_hir::definitions::Definitions;
+use rustc_hir::Crate;
+use rustc_lint::LintStore;
+use rustc_middle::arena::Arena;
+use rustc_middle::dep_graph::DepGraph;
+use rustc_middle::middle;
+use rustc_middle::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn};
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::steal::Steal;
+use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt};
+use rustc_mir as mir;
+use rustc_mir_build as mir_build;
+use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str};
+use rustc_passes::{self, hir_stats, layout_test};
+use rustc_plugin_impl as plugin;
+use rustc_resolve::{Resolver, ResolverArenas};
+use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType, PpMode, PpSourceMode};
+use rustc_session::output::{filename_for_input, filename_for_metadata};
+use rustc_session::search_paths::PathKind;
+use rustc_session::Session;
+use rustc_span::symbol::Symbol;
+use rustc_span::{FileName, RealFileName};
+use rustc_trait_selection::traits;
+use rustc_typeck as typeck;
+use tracing::{info, warn};
+
+use rustc_serialize::json;
+use tempfile::Builder as TempFileBuilder;
+
+use std::any::Any;
+use std::cell::RefCell;
+use std::ffi::OsString;
+use std::io::{self, BufWriter, Write};
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::{env, fs, iter, mem};
+
+pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> {
+    let krate = sess.time("parse_crate", || match input {
+        Input::File(file) => parse_crate_from_file(file, &sess.parse_sess),
+        Input::Str { input, name } => {
+            parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess)
+        }
+    })?;
+
+    if sess.opts.debugging_opts.ast_json_noexpand {
+        println!("{}", json::as_json(&krate));
+    }
+
+    if sess.opts.debugging_opts.input_stats {
+        println!("Lines of code:             {}", sess.source_map().count_lines());
+        println!("Pre-expansion node count:  {}", count_nodes(&krate));
+    }
+
+    if let Some(ref s) = sess.opts.debugging_opts.show_span {
+        rustc_ast_passes::show_span::run(sess.diagnostic(), s, &krate);
+    }
+
+    if sess.opts.debugging_opts.hir_stats {
+        hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS");
+    }
+
+    Ok(krate)
+}
+
+fn count_nodes(krate: &ast::Crate) -> usize {
+    let mut counter = rustc_ast_passes::node_count::NodeCounter::new();
+    visit::walk_crate(&mut counter, krate);
+    counter.count
+}
+
+declare_box_region_type!(
+    pub BoxedResolver,
+    for(),
+    (&mut Resolver<'_>) -> (Result<ast::Crate>, ResolverOutputs)
+);
+
+/// Runs the "early phases" of the compiler: initial `cfg` processing, loading compiler plugins,
+/// syntax expansion, secondary `cfg` expansion, synthesis of a test
+/// harness if one is to be provided, injection of a dependency on the
+/// standard library and prelude, and name resolution.
+///
+/// Returns `None` if we're aborting after handling -W help.
+pub fn configure_and_expand(
+    sess: Lrc<Session>,
+    lint_store: Lrc<LintStore>,
+    metadata_loader: Box<MetadataLoaderDyn>,
+    krate: ast::Crate,
+    crate_name: &str,
+) -> Result<(ast::Crate, BoxedResolver)> {
+    tracing::trace!("configure_and_expand");
+    // Currently, we ignore the name resolution data structures for the purposes of dependency
+    // tracking. Instead we will run name resolution and include its output in the hash of each
+    // item, much like we do for macro expansion. In other words, the hash reflects not just
+    // its contents but the results of name resolution on those contents. Hopefully we'll push
+    // this back at some point.
+    let crate_name = crate_name.to_string();
+    let (result, resolver) = BoxedResolver::new(static move |mut action| {
+        let _ = action;
+        let sess = &*sess;
+        let resolver_arenas = Resolver::arenas();
+        let res = configure_and_expand_inner(
+            sess,
+            &lint_store,
+            krate,
+            &crate_name,
+            &resolver_arenas,
+            &*metadata_loader,
+        );
+        let mut resolver = match res {
+            Err(v) => {
+                yield BoxedResolver::initial_yield(Err(v));
+                panic!()
+            }
+            Ok((krate, resolver)) => {
+                action = yield BoxedResolver::initial_yield(Ok(krate));
+                resolver
+            }
+        };
+        box_region_allow_access!(for(), (&mut Resolver<'_>), (&mut resolver), action);
+        resolver.into_outputs()
+    });
+    result.map(|k| (k, resolver))
+}
+
+impl BoxedResolver {
+    pub fn to_resolver_outputs(resolver: Rc<RefCell<BoxedResolver>>) -> ResolverOutputs {
+        match Rc::try_unwrap(resolver) {
+            Ok(resolver) => resolver.into_inner().complete(),
+            Err(resolver) => resolver.borrow_mut().access(|resolver| resolver.clone_outputs()),
+        }
+    }
+}
+
+pub fn register_plugins<'a>(
+    sess: &'a Session,
+    metadata_loader: &'a dyn MetadataLoader,
+    register_lints: impl Fn(&Session, &mut LintStore),
+    mut krate: ast::Crate,
+    crate_name: &str,
+) -> Result<(ast::Crate, Lrc<LintStore>)> {
+    krate = sess.time("attributes_injection", || {
+        rustc_builtin_macros::cmdline_attrs::inject(
+            krate,
+            &sess.parse_sess,
+            &sess.opts.debugging_opts.crate_attr,
+        )
+    });
+
+    let (krate, features) = rustc_expand::config::features(sess, krate);
+    // these need to be set "early" so that expansion sees `quote` if enabled.
+    sess.init_features(features);
+
+    let crate_types = util::collect_crate_types(sess, &krate.attrs);
+    sess.init_crate_types(crate_types);
+
+    let disambiguator = util::compute_crate_disambiguator(sess);
+    sess.crate_disambiguator.set(disambiguator).expect("not yet initialized");
+    rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator);
+
+    if sess.opts.incremental.is_some() {
+        sess.time("incr_comp_garbage_collect_session_directories", || {
+            if let Err(e) = rustc_incremental::garbage_collect_session_directories(sess) {
+                warn!(
+                    "Error while trying to garbage collect incremental \
+                     compilation cache directory: {}",
+                    e
+                );
+            }
+        });
+    }
+
+    sess.time("recursion_limit", || {
+        middle::limits::update_limits(sess, &krate);
+    });
+
+    let mut lint_store = rustc_lint::new_lint_store(
+        sess.opts.debugging_opts.no_interleave_lints,
+        sess.unstable_options(),
+    );
+    register_lints(&sess, &mut lint_store);
+
+    let registrars =
+        sess.time("plugin_loading", || plugin::load::load_plugins(sess, metadata_loader, &krate));
+    sess.time("plugin_registration", || {
+        let mut registry = plugin::Registry { lint_store: &mut lint_store };
+        for registrar in registrars {
+            registrar(&mut registry);
+        }
+    });
+
+    Ok((krate, Lrc::new(lint_store)))
+}
+
+fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) {
+    sess.time("pre_AST_expansion_lint_checks", || {
+        rustc_lint::check_ast_crate(
+            sess,
+            lint_store,
+            &krate,
+            true,
+            None,
+            rustc_lint::BuiltinCombinedPreExpansionLintPass::new(),
+        );
+    });
+}
+
+fn configure_and_expand_inner<'a>(
+    sess: &'a Session,
+    lint_store: &'a LintStore,
+    mut krate: ast::Crate,
+    crate_name: &str,
+    resolver_arenas: &'a ResolverArenas<'a>,
+    metadata_loader: &'a MetadataLoaderDyn,
+) -> Result<(ast::Crate, Resolver<'a>)> {
+    tracing::trace!("configure_and_expand_inner");
+    pre_expansion_lint(sess, lint_store, &krate);
+
+    let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas);
+    rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition());
+
+    krate = sess.time("crate_injection", || {
+        let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s));
+        let (krate, name) = rustc_builtin_macros::standard_library_imports::inject(
+            krate,
+            &mut resolver,
+            &sess,
+            alt_std_name,
+        );
+        if let Some(name) = name {
+            sess.parse_sess.injected_crate_name.set(name).expect("not yet initialized");
+        }
+        krate
+    });
+
+    util::check_attr_crate_type(&sess, &krate.attrs, &mut resolver.lint_buffer());
+
+    // Expand all macros
+    krate = sess.time("macro_expand_crate", || {
+        // Windows dlls do not have rpaths, so they don't know how to find their
+        // dependencies. It's up to us to tell the system where to find all the
+        // dependent dlls. Note that this uses cfg!(windows) as opposed to
+        // targ_cfg because syntax extensions are always loaded for the host
+        // compiler, not for the target.
+        //
+        // This is somewhat of an inherently racy operation, however, as
+        // multiple threads calling this function could possibly continue
+        // extending PATH far beyond what it should. To solve this for now we
+        // just don't add any new elements to PATH which are already there
+        // within PATH. This is basically a targeted fix at #17360 for rustdoc
+        // which runs rustc in parallel but has been seen (#33844) to cause
+        // problems with PATH becoming too long.
+        let mut old_path = OsString::new();
+        if cfg!(windows) {
+            old_path = env::var_os("PATH").unwrap_or(old_path);
+            let mut new_path = sess.host_filesearch(PathKind::All).search_path_dirs();
+            for path in env::split_paths(&old_path) {
+                if !new_path.contains(&path) {
+                    new_path.push(path);
+                }
+            }
+            env::set_var(
+                "PATH",
+                &env::join_paths(
+                    new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()),
+                )
+                .unwrap(),
+            );
+        }
+
+        // Create the config for macro expansion
+        let features = sess.features_untracked();
+        let cfg = rustc_expand::expand::ExpansionConfig {
+            features: Some(&features),
+            recursion_limit: sess.recursion_limit(),
+            trace_mac: sess.opts.debugging_opts.trace_macros,
+            should_test: sess.opts.test,
+            span_debug: sess.opts.debugging_opts.span_debug,
+            ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
+        };
+
+        let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k);
+        let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded));
+
+        // Expand macros now!
+        let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
+
+        // The rest is error reporting
+
+        sess.time("check_unused_macros", || {
+            ecx.check_unused_macros();
+        });
+
+        if cfg!(windows) {
+            env::set_var("PATH", &old_path);
+        }
+
+        let recursion_limit_hit = ecx.reduced_recursion_limit.is_some();
+        if recursion_limit_hit {
+            // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed
+            // with a large AST
+            Err(ErrorReported)
+        } else {
+            Ok(krate)
+        }
+    })?;
+
+    sess.time("maybe_building_test_harness", || {
+        rustc_builtin_macros::test_harness::inject(&sess, &mut resolver, &mut krate)
+    });
+
+    if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty {
+        tracing::debug!("replacing bodies with loop {{}}");
+        util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate);
+    }
+
+    let has_proc_macro_decls = sess.time("AST_validation", || {
+        rustc_ast_passes::ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer())
+    });
+
+    let crate_types = sess.crate_types();
+    let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
+
+    // For backwards compatibility, we don't try to run proc macro injection
+    // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being
+    // specified. This should only affect users who manually invoke 'rustdoc', as
+    // 'cargo doc' will automatically pass the proper '--crate-type' flags.
+    // However, we do emit a warning, to let such users know that they should
+    // start passing '--crate-type proc-macro'
+    if has_proc_macro_decls && sess.opts.actually_rustdoc && !is_proc_macro_crate {
+        let mut msg = sess.diagnostic().struct_warn(
+            &"Trying to document proc macro crate \
+            without passing '--crate-type proc-macro to rustdoc",
+        );
+
+        msg.warn("The generated documentation may be incorrect");
+        msg.emit()
+    } else {
+        krate = sess.time("maybe_create_a_macro_crate", || {
+            let num_crate_types = crate_types.len();
+            let is_test_crate = sess.opts.test;
+            rustc_builtin_macros::proc_macro_harness::inject(
+                &sess,
+                &mut resolver,
+                krate,
+                is_proc_macro_crate,
+                has_proc_macro_decls,
+                is_test_crate,
+                num_crate_types,
+                sess.diagnostic(),
+            )
+        });
+    }
+
+    // Done with macro expansion!
+
+    if sess.opts.debugging_opts.input_stats {
+        println!("Post-expansion node count: {}", count_nodes(&krate));
+    }
+
+    if sess.opts.debugging_opts.hir_stats {
+        hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS");
+    }
+
+    if sess.opts.debugging_opts.ast_json {
+        println!("{}", json::as_json(&krate));
+    }
+
+    resolver.resolve_crate(&krate);
+
+    // Needs to go *after* expansion to be able to check the results of macro expansion.
+    sess.time("complete_gated_feature_checking", || {
+        rustc_ast_passes::feature_gate::check_crate(&krate, sess);
+    });
+
+    // Add all buffered lints from the `ParseSess` to the `Session`.
+    sess.parse_sess.buffered_lints.with_lock(|buffered_lints| {
+        info!("{} parse sess buffered_lints", buffered_lints.len());
+        for early_lint in buffered_lints.drain(..) {
+            resolver.lint_buffer().add_early_lint(early_lint);
+        }
+    });
+
+    Ok((krate, resolver))
+}
+
+pub fn lower_to_hir<'res, 'tcx>(
+    sess: &'tcx Session,
+    lint_store: &LintStore,
+    resolver: &'res mut Resolver<'_>,
+    dep_graph: &'res DepGraph,
+    krate: &'res ast::Crate,
+    arena: &'tcx rustc_ast_lowering::Arena<'tcx>,
+) -> Crate<'tcx> {
+    // 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();
+
+    // Lower AST to HIR.
+    let hir_crate = rustc_ast_lowering::lower_crate(
+        sess,
+        &krate,
+        resolver,
+        rustc_parse::nt_to_tokenstream,
+        arena,
+    );
+
+    if sess.opts.debugging_opts.hir_stats {
+        hir_stats::print_hir_stats(&hir_crate);
+    }
+
+    sess.time("early_lint_checks", || {
+        rustc_lint::check_ast_crate(
+            sess,
+            lint_store,
+            &krate,
+            false,
+            Some(std::mem::take(resolver.lint_buffer())),
+            rustc_lint::BuiltinCombinedEarlyLintPass::new(),
+        )
+    });
+
+    // Discard hygiene data, which isn't required after lowering to HIR.
+    if !sess.opts.debugging_opts.keep_hygiene_data {
+        rustc_span::hygiene::clear_syntax_context_map();
+    }
+
+    hir_crate
+}
+
+// Returns all the paths that correspond to generated files.
+fn generated_output_paths(
+    sess: &Session,
+    outputs: &OutputFilenames,
+    exact_name: bool,
+    crate_name: &str,
+) -> Vec<PathBuf> {
+    let mut out_filenames = Vec::new();
+    for output_type in sess.opts.output_types.keys() {
+        let file = outputs.path(*output_type);
+        match *output_type {
+            // If the filename has been overridden using `-o`, it will not be modified
+            // by appending `.rlib`, `.exe`, etc., so we can skip this transformation.
+            OutputType::Exe if !exact_name => {
+                for crate_type in sess.crate_types().iter() {
+                    let p = filename_for_input(sess, *crate_type, crate_name, outputs);
+                    out_filenames.push(p);
+                }
+            }
+            OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => {
+                // Don't add the dep-info output when omitting it from dep-info targets
+            }
+            _ => {
+                out_filenames.push(file);
+            }
+        }
+    }
+    out_filenames
+}
+
+// Runs `f` on every output file path and returns the first non-None result, or None if `f`
+// returns None for every file path.
+fn check_output<F, T>(output_paths: &[PathBuf], f: F) -> Option<T>
+where
+    F: Fn(&PathBuf) -> Option<T>,
+{
+    for output_path in output_paths {
+        if let Some(result) = f(output_path) {
+            return Some(result);
+        }
+    }
+    None
+}
+
+fn output_contains_path(output_paths: &[PathBuf], input_path: &PathBuf) -> bool {
+    let input_path = input_path.canonicalize().ok();
+    if input_path.is_none() {
+        return false;
+    }
+    let check = |output_path: &PathBuf| {
+        if output_path.canonicalize().ok() == input_path { Some(()) } else { None }
+    };
+    check_output(output_paths, check).is_some()
+}
+
+fn output_conflicts_with_dir(output_paths: &[PathBuf]) -> Option<PathBuf> {
+    let check = |output_path: &PathBuf| output_path.is_dir().then(|| output_path.clone());
+    check_output(output_paths, check)
+}
+
+fn escape_dep_filename(filename: &FileName) -> String {
+    // Apparently clang and gcc *only* escape spaces:
+    // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4
+    filename.to_string().replace(" ", "\\ ")
+}
+
+// Makefile comments only need escaping newlines and `\`.
+// The result can be unescaped by anything that can unescape `escape_default` and friends.
+fn escape_dep_env(symbol: Symbol) -> String {
+    let s = symbol.as_str();
+    let mut escaped = String::with_capacity(s.len());
+    for c in s.chars() {
+        match c {
+            '\n' => escaped.push_str(r"\n"),
+            '\r' => escaped.push_str(r"\r"),
+            '\\' => escaped.push_str(r"\\"),
+            _ => escaped.push(c),
+        }
+    }
+    escaped
+}
+
+fn write_out_deps(
+    sess: &Session,
+    boxed_resolver: &Steal<Rc<RefCell<BoxedResolver>>>,
+    outputs: &OutputFilenames,
+    out_filenames: &[PathBuf],
+) {
+    // Write out dependency rules to the dep-info file if requested
+    if !sess.opts.output_types.contains_key(&OutputType::DepInfo) {
+        return;
+    }
+    let deps_filename = outputs.path(OutputType::DepInfo);
+
+    let result = (|| -> io::Result<()> {
+        // Build a list of files used to compile the output and
+        // write Makefile-compatible dependency rules
+        let mut files: Vec<String> = sess
+            .source_map()
+            .files()
+            .iter()
+            .filter(|fmap| fmap.is_real_file())
+            .filter(|fmap| !fmap.is_imported())
+            .map(|fmap| escape_dep_filename(&fmap.unmapped_path.as_ref().unwrap_or(&fmap.name)))
+            .collect();
+
+        if sess.binary_dep_depinfo() {
+            boxed_resolver.borrow().borrow_mut().access(|resolver| {
+                for cnum in resolver.cstore().crates_untracked() {
+                    let source = resolver.cstore().crate_source_untracked(cnum);
+                    if let Some((path, _)) = source.dylib {
+                        let file_name = FileName::Real(RealFileName::Named(path));
+                        files.push(escape_dep_filename(&file_name));
+                    }
+                    if let Some((path, _)) = source.rlib {
+                        let file_name = FileName::Real(RealFileName::Named(path));
+                        files.push(escape_dep_filename(&file_name));
+                    }
+                    if let Some((path, _)) = source.rmeta {
+                        let file_name = FileName::Real(RealFileName::Named(path));
+                        files.push(escape_dep_filename(&file_name));
+                    }
+                }
+            });
+        }
+
+        let mut file = BufWriter::new(fs::File::create(&deps_filename)?);
+        for path in out_filenames {
+            writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
+        }
+
+        // Emit a fake target for each input file to the compilation. This
+        // prevents `make` from spitting out an error if a file is later
+        // deleted. For more info see #28735
+        for path in files {
+            writeln!(file, "{}:", path)?;
+        }
+
+        // Emit special comments with information about accessed environment variables.
+        let env_depinfo = sess.parse_sess.env_depinfo.borrow();
+        if !env_depinfo.is_empty() {
+            let mut envs: Vec<_> = env_depinfo
+                .iter()
+                .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
+                .collect();
+            envs.sort_unstable();
+            writeln!(file)?;
+            for (k, v) in envs {
+                write!(file, "# env-dep:{}", k)?;
+                if let Some(v) = v {
+                    write!(file, "={}", v)?;
+                }
+                writeln!(file)?;
+            }
+        }
+
+        Ok(())
+    })();
+
+    match result {
+        Ok(_) => {
+            if sess.opts.json_artifact_notifications {
+                sess.parse_sess
+                    .span_diagnostic
+                    .emit_artifact_notification(&deps_filename, "dep-info");
+            }
+        }
+        Err(e) => sess.fatal(&format!(
+            "error writing dependencies to `{}`: {}",
+            deps_filename.display(),
+            e
+        )),
+    }
+}
+
+pub fn prepare_outputs(
+    sess: &Session,
+    compiler: &Compiler,
+    krate: &ast::Crate,
+    boxed_resolver: &Steal<Rc<RefCell<BoxedResolver>>>,
+    crate_name: &str,
+) -> Result<OutputFilenames> {
+    let _timer = sess.timer("prepare_outputs");
+
+    // FIXME: rustdoc passes &[] instead of &krate.attrs here
+    let outputs = util::build_output_filenames(
+        &compiler.input,
+        &compiler.output_dir,
+        &compiler.output_file,
+        &krate.attrs,
+        sess,
+    );
+
+    let output_paths =
+        generated_output_paths(sess, &outputs, compiler.output_file.is_some(), &crate_name);
+
+    // Ensure the source file isn't accidentally overwritten during compilation.
+    if let Some(ref input_path) = compiler.input_path {
+        if sess.opts.will_create_output_file() {
+            if output_contains_path(&output_paths, input_path) {
+                sess.err(&format!(
+                    "the input file \"{}\" would be overwritten by the generated \
+                        executable",
+                    input_path.display()
+                ));
+                return Err(ErrorReported);
+            }
+            if let Some(dir_path) = output_conflicts_with_dir(&output_paths) {
+                sess.err(&format!(
+                    "the generated executable for the input file \"{}\" conflicts with the \
+                        existing directory \"{}\"",
+                    input_path.display(),
+                    dir_path.display()
+                ));
+                return Err(ErrorReported);
+            }
+        }
+    }
+
+    write_out_deps(sess, boxed_resolver, &outputs, &output_paths);
+
+    let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo)
+        && sess.opts.output_types.len() == 1;
+
+    if !only_dep_info {
+        if let Some(ref dir) = compiler.output_dir {
+            if fs::create_dir_all(dir).is_err() {
+                sess.err("failed to find or create the directory specified by `--out-dir`");
+                return Err(ErrorReported);
+            }
+        }
+    }
+
+    Ok(outputs)
+}
+
+pub static DEFAULT_QUERY_PROVIDERS: Lazy<Providers> = Lazy::new(|| {
+    let providers = &mut Providers::default();
+    providers.analysis = analysis;
+    proc_macro_decls::provide(providers);
+    plugin::build::provide(providers);
+    rustc_middle::hir::provide(providers);
+    mir::provide(providers);
+    mir_build::provide(providers);
+    rustc_privacy::provide(providers);
+    typeck::provide(providers);
+    ty::provide(providers);
+    traits::provide(providers);
+    rustc_passes::provide(providers);
+    rustc_resolve::provide(providers);
+    rustc_traits::provide(providers);
+    rustc_ty::provide(providers);
+    rustc_metadata::provide(providers);
+    rustc_lint::provide(providers);
+    rustc_symbol_mangling::provide(providers);
+    rustc_codegen_ssa::provide(providers);
+    *providers
+});
+
+pub static DEFAULT_EXTERN_QUERY_PROVIDERS: Lazy<Providers> = Lazy::new(|| {
+    let mut extern_providers = *DEFAULT_QUERY_PROVIDERS;
+    rustc_metadata::provide_extern(&mut extern_providers);
+    rustc_codegen_ssa::provide_extern(&mut extern_providers);
+    extern_providers
+});
+
+pub struct QueryContext<'tcx>(&'tcx GlobalCtxt<'tcx>);
+
+impl<'tcx> QueryContext<'tcx> {
+    pub fn enter<F, R>(&mut self, f: F) -> R
+    where
+        F: FnOnce(TyCtxt<'tcx>) -> R,
+    {
+        let icx = ty::tls::ImplicitCtxt::new(self.0);
+        ty::tls::enter_context(&icx, |_| f(icx.tcx))
+    }
+
+    pub fn print_stats(&mut self) {
+        self.enter(ty::query::print_stats)
+    }
+}
+
+pub fn create_global_ctxt<'tcx>(
+    compiler: &'tcx Compiler,
+    lint_store: Lrc<LintStore>,
+    krate: &'tcx Crate<'tcx>,
+    dep_graph: DepGraph,
+    mut resolver_outputs: ResolverOutputs,
+    outputs: OutputFilenames,
+    crate_name: &str,
+    global_ctxt: &'tcx OnceCell<GlobalCtxt<'tcx>>,
+    arena: &'tcx WorkerLocal<Arena<'tcx>>,
+) -> QueryContext<'tcx> {
+    let sess = &compiler.session();
+    let defs: &'tcx Definitions = arena.alloc(mem::replace(
+        &mut resolver_outputs.definitions,
+        Definitions::new(crate_name, sess.local_crate_disambiguator()),
+    ));
+
+    let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess);
+
+    let codegen_backend = compiler.codegen_backend();
+    let mut local_providers = *DEFAULT_QUERY_PROVIDERS;
+    codegen_backend.provide(&mut local_providers);
+
+    let mut extern_providers = *DEFAULT_EXTERN_QUERY_PROVIDERS;
+    codegen_backend.provide(&mut extern_providers);
+    codegen_backend.provide_extern(&mut extern_providers);
+
+    if let Some(callback) = compiler.override_queries {
+        callback(sess, &mut local_providers, &mut extern_providers);
+    }
+
+    let gcx = sess.time("setup_global_ctxt", || {
+        global_ctxt.get_or_init(|| {
+            TyCtxt::create_global_ctxt(
+                sess,
+                lint_store,
+                local_providers,
+                extern_providers,
+                arena,
+                resolver_outputs,
+                krate,
+                defs,
+                dep_graph,
+                query_result_on_disk_cache,
+                &crate_name,
+                &outputs,
+            )
+        })
+    });
+
+    // Do some initialization of the DepGraph that can only be done with the tcx available.
+    let icx = ty::tls::ImplicitCtxt::new(&gcx);
+    ty::tls::enter_context(&icx, |_| {
+        icx.tcx.sess.time("dep_graph_tcx_init", || rustc_incremental::dep_graph_tcx_init(icx.tcx));
+    });
+
+    QueryContext(gcx)
+}
+
+/// Runs the resolution, type-checking, region checking and other
+/// miscellaneous analysis passes on the crate.
+fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
+    assert_eq!(cnum, LOCAL_CRATE);
+
+    rustc_passes::hir_id_validator::check_crate(tcx);
+
+    let sess = tcx.sess;
+    let mut entry_point = None;
+
+    sess.time("misc_checking_1", || {
+        parallel!(
+            {
+                entry_point = sess
+                    .time("looking_for_entry_point", || rustc_passes::entry::find_entry_point(tcx));
+
+                sess.time("looking_for_plugin_registrar", || {
+                    plugin::build::find_plugin_registrar(tcx)
+                });
+
+                sess.time("looking_for_derive_registrar", || proc_macro_decls::find(tcx));
+            },
+            {
+                par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
+                    let local_def_id = tcx.hir().local_def_id(module);
+                    tcx.ensure().check_mod_loops(local_def_id);
+                    tcx.ensure().check_mod_attrs(local_def_id);
+                    tcx.ensure().check_mod_unstable_api_usage(local_def_id);
+                    tcx.ensure().check_mod_const_bodies(local_def_id);
+                });
+            }
+        );
+    });
+
+    // passes are timed inside typeck
+    typeck::check_crate(tcx)?;
+
+    sess.time("misc_checking_2", || {
+        parallel!(
+            {
+                sess.time("match_checking", || {
+                    tcx.par_body_owners(|def_id| {
+                        tcx.ensure().check_match(def_id.to_def_id());
+                    });
+                });
+            },
+            {
+                sess.time("liveness_and_intrinsic_checking", || {
+                    par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
+                        // this must run before MIR dump, because
+                        // "not all control paths return a value" is reported here.
+                        //
+                        // maybe move the check to a MIR pass?
+                        let local_def_id = tcx.hir().local_def_id(module);
+
+                        tcx.ensure().check_mod_liveness(local_def_id);
+                        tcx.ensure().check_mod_intrinsics(local_def_id);
+                    });
+                });
+            }
+        );
+    });
+
+    sess.time("MIR_borrow_checking", || {
+        tcx.par_body_owners(|def_id| tcx.ensure().mir_borrowck(def_id));
+    });
+
+    sess.time("MIR_effect_checking", || {
+        for def_id in tcx.body_owners() {
+            mir::transform::check_unsafety::check_unsafety(tcx, def_id);
+
+            if tcx.hir().body_const_context(def_id).is_some() {
+                tcx.ensure()
+                    .mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(def_id));
+            }
+        }
+    });
+
+    sess.time("layout_testing", || layout_test::test_layout(tcx));
+
+    // Avoid overwhelming user with errors if borrow checking failed.
+    // I'm not sure how helpful this is, to be honest, but it avoids a
+    // lot of annoying errors in the compile-fail tests (basically,
+    // lint warnings and so on -- kindck used to do this abort, but
+    // kindck is gone now). -nmatsakis
+    if sess.has_errors() {
+        return Err(ErrorReported);
+    }
+
+    sess.time("misc_checking_3", || {
+        parallel!(
+            {
+                tcx.ensure().privacy_access_levels(LOCAL_CRATE);
+
+                parallel!(
+                    {
+                        tcx.ensure().check_private_in_public(LOCAL_CRATE);
+                    },
+                    {
+                        sess.time("death_checking", || rustc_passes::dead::check_crate(tcx));
+                    },
+                    {
+                        sess.time("unused_lib_feature_checking", || {
+                            rustc_passes::stability::check_unused_or_stable_features(tcx)
+                        });
+                    },
+                    {
+                        sess.time("lint_checking", || {
+                            rustc_lint::check_crate(tcx, || {
+                                rustc_lint::BuiltinCombinedLateLintPass::new()
+                            });
+                        });
+                    }
+                );
+            },
+            {
+                sess.time("privacy_checking_modules", || {
+                    par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
+                        tcx.ensure().check_mod_privacy(tcx.hir().local_def_id(module));
+                    });
+                });
+            }
+        );
+    });
+
+    Ok(())
+}
+
+fn encode_and_write_metadata(
+    tcx: TyCtxt<'_>,
+    outputs: &OutputFilenames,
+) -> (middle::cstore::EncodedMetadata, bool) {
+    #[derive(PartialEq, Eq, PartialOrd, Ord)]
+    enum MetadataKind {
+        None,
+        Uncompressed,
+        Compressed,
+    }
+
+    let metadata_kind = tcx
+        .sess
+        .crate_types()
+        .iter()
+        .map(|ty| match *ty {
+            CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None,
+
+            CrateType::Rlib => MetadataKind::Uncompressed,
+
+            CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed,
+        })
+        .max()
+        .unwrap_or(MetadataKind::None);
+
+    let metadata = match metadata_kind {
+        MetadataKind::None => middle::cstore::EncodedMetadata::new(),
+        MetadataKind::Uncompressed | MetadataKind::Compressed => tcx.encode_metadata(),
+    };
+
+    let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
+
+    let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
+    if need_metadata_file {
+        let crate_name = &tcx.crate_name(LOCAL_CRATE).as_str();
+        let out_filename = filename_for_metadata(tcx.sess, crate_name, outputs);
+        // To avoid races with another rustc process scanning the output directory,
+        // we need to write the file somewhere else and atomically move it to its
+        // final destination, with an `fs::rename` call. In order for the rename to
+        // always succeed, the temporary file needs to be on the same filesystem,
+        // which is why we create it inside the output directory specifically.
+        let metadata_tmpdir = TempFileBuilder::new()
+            .prefix("rmeta")
+            .tempdir_in(out_filename.parent().unwrap())
+            .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
+        let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
+        let metadata_filename = emit_metadata(tcx.sess, &metadata, &metadata_tmpdir);
+        if let Err(e) = fs::rename(&metadata_filename, &out_filename) {
+            tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
+        }
+        if tcx.sess.opts.json_artifact_notifications {
+            tcx.sess
+                .parse_sess
+                .span_diagnostic
+                .emit_artifact_notification(&out_filename, "metadata");
+        }
+    }
+
+    let need_metadata_module = metadata_kind == MetadataKind::Compressed;
+
+    (metadata, need_metadata_module)
+}
+
+/// Runs the codegen backend, after which the AST and analysis can
+/// be discarded.
+pub fn start_codegen<'tcx>(
+    codegen_backend: &dyn CodegenBackend,
+    tcx: TyCtxt<'tcx>,
+    outputs: &OutputFilenames,
+) -> Box<dyn Any> {
+    info!("Pre-codegen\n{:?}", tcx.debug_stats());
+
+    let (metadata, need_metadata_module) = encode_and_write_metadata(tcx, outputs);
+
+    let codegen = tcx.sess.time("codegen_crate", move || {
+        codegen_backend.codegen_crate(tcx, metadata, need_metadata_module)
+    });
+
+    info!("Post-codegen\n{:?}", tcx.debug_stats());
+
+    if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) {
+        if let Err(e) = mir::transform::dump_mir::emit_mir(tcx, outputs) {
+            tcx.sess.err(&format!("could not emit MIR: {}", e));
+            tcx.sess.abort_if_errors();
+        }
+    }
+
+    codegen
+}
diff --git a/compiler/rustc_interface/src/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs
new file mode 100644
index 00000000000..d56115fd6ac
--- /dev/null
+++ b/compiler/rustc_interface/src/proc_macro_decls.rs
@@ -0,0 +1,40 @@
+use rustc_hir as hir;
+use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
+use rustc_hir::itemlikevisit::ItemLikeVisitor;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::sym;
+
+pub fn find(tcx: TyCtxt<'_>) -> Option<DefId> {
+    tcx.proc_macro_decls_static(LOCAL_CRATE)
+}
+
+fn proc_macro_decls_static(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<DefId> {
+    assert_eq!(cnum, LOCAL_CRATE);
+
+    let mut finder = Finder { tcx, decls: None };
+    tcx.hir().krate().visit_all_item_likes(&mut finder);
+
+    finder.decls.map(|id| tcx.hir().local_def_id(id).to_def_id())
+}
+
+struct Finder<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    decls: Option<hir::HirId>,
+}
+
+impl<'v> ItemLikeVisitor<'v> for Finder<'_> {
+    fn visit_item(&mut self, item: &hir::Item<'_>) {
+        if self.tcx.sess.contains_name(&item.attrs, sym::rustc_proc_macro_decls) {
+            self.decls = Some(item.hir_id);
+        }
+    }
+
+    fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
+
+    fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
+}
+
+pub(crate) fn provide(providers: &mut Providers) {
+    *providers = Providers { proc_macro_decls_static, ..*providers };
+}
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
new file mode 100644
index 00000000000..8b82217a91a
--- /dev/null
+++ b/compiler/rustc_interface/src/queries.rs
@@ -0,0 +1,397 @@
+use crate::interface::{Compiler, Result};
+use crate::passes::{self, BoxedResolver, QueryContext};
+
+use rustc_ast as ast;
+use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
+use rustc_errors::ErrorReported;
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_hir::Crate;
+use rustc_incremental::DepGraphFuture;
+use rustc_lint::LintStore;
+use rustc_middle::arena::Arena;
+use rustc_middle::dep_graph::DepGraph;
+use rustc_middle::ty::steal::Steal;
+use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt};
+use rustc_session::config::{OutputFilenames, OutputType};
+use rustc_session::{output::find_crate_name, Session};
+use rustc_span::symbol::sym;
+use std::any::Any;
+use std::cell::{Ref, RefCell, RefMut};
+use std::rc::Rc;
+
+/// Represent the result of a query.
+/// This result can be stolen with the `take` method and generated with the `compute` method.
+pub struct Query<T> {
+    result: RefCell<Option<Result<T>>>,
+}
+
+impl<T> Query<T> {
+    fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<&Query<T>> {
+        let mut result = self.result.borrow_mut();
+        if result.is_none() {
+            *result = Some(f());
+        }
+        result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err)
+    }
+
+    /// Takes ownership of the query result. Further attempts to take or peek the query
+    /// result will panic unless it is generated by calling the `compute` method.
+    pub fn take(&self) -> T {
+        self.result.borrow_mut().take().expect("missing query result").unwrap()
+    }
+
+    /// Borrows the query result using the RefCell. Panics if the result is stolen.
+    pub fn peek(&self) -> Ref<'_, T> {
+        Ref::map(self.result.borrow(), |r| {
+            r.as_ref().unwrap().as_ref().expect("missing query result")
+        })
+    }
+
+    /// Mutably borrows the query result using the RefCell. Panics if the result is stolen.
+    pub fn peek_mut(&self) -> RefMut<'_, T> {
+        RefMut::map(self.result.borrow_mut(), |r| {
+            r.as_mut().unwrap().as_mut().expect("missing query result")
+        })
+    }
+}
+
+impl<T> Default for Query<T> {
+    fn default() -> Self {
+        Query { result: RefCell::new(None) }
+    }
+}
+
+pub struct Queries<'tcx> {
+    compiler: &'tcx Compiler,
+    gcx: OnceCell<GlobalCtxt<'tcx>>,
+
+    arena: WorkerLocal<Arena<'tcx>>,
+    hir_arena: WorkerLocal<rustc_ast_lowering::Arena<'tcx>>,
+
+    dep_graph_future: Query<Option<DepGraphFuture>>,
+    parse: Query<ast::Crate>,
+    crate_name: Query<String>,
+    register_plugins: Query<(ast::Crate, Lrc<LintStore>)>,
+    expansion: Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>, Lrc<LintStore>)>,
+    dep_graph: Query<DepGraph>,
+    lower_to_hir: Query<(&'tcx Crate<'tcx>, Steal<ResolverOutputs>)>,
+    prepare_outputs: Query<OutputFilenames>,
+    global_ctxt: Query<QueryContext<'tcx>>,
+    ongoing_codegen: Query<Box<dyn Any>>,
+}
+
+impl<'tcx> Queries<'tcx> {
+    pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> {
+        Queries {
+            compiler,
+            gcx: OnceCell::new(),
+            arena: WorkerLocal::new(|_| Arena::default()),
+            hir_arena: WorkerLocal::new(|_| rustc_ast_lowering::Arena::default()),
+            dep_graph_future: Default::default(),
+            parse: Default::default(),
+            crate_name: Default::default(),
+            register_plugins: Default::default(),
+            expansion: Default::default(),
+            dep_graph: Default::default(),
+            lower_to_hir: Default::default(),
+            prepare_outputs: Default::default(),
+            global_ctxt: Default::default(),
+            ongoing_codegen: Default::default(),
+        }
+    }
+
+    fn session(&self) -> &Lrc<Session> {
+        &self.compiler.sess
+    }
+    fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> {
+        &self.compiler.codegen_backend()
+    }
+
+    pub fn dep_graph_future(&self) -> Result<&Query<Option<DepGraphFuture>>> {
+        self.dep_graph_future.compute(|| {
+            Ok(self
+                .session()
+                .opts
+                .build_dep_graph()
+                .then(|| rustc_incremental::load_dep_graph(self.session())))
+        })
+    }
+
+    pub fn parse(&self) -> Result<&Query<ast::Crate>> {
+        self.parse.compute(|| {
+            passes::parse(self.session(), &self.compiler.input).map_err(|mut parse_error| {
+                parse_error.emit();
+                ErrorReported
+            })
+        })
+    }
+
+    pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc<LintStore>)>> {
+        self.register_plugins.compute(|| {
+            let crate_name = self.crate_name()?.peek().clone();
+            let krate = self.parse()?.take();
+
+            let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
+            let result = passes::register_plugins(
+                self.session(),
+                &*self.codegen_backend().metadata_loader(),
+                self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
+                krate,
+                &crate_name,
+            );
+
+            // Compute the dependency graph (in the background). We want to do
+            // this as early as possible, to give the DepGraph maximum time to
+            // load before dep_graph() is called, but it also can't happen
+            // until after rustc_incremental::prepare_session_directory() is
+            // called, which happens within passes::register_plugins().
+            self.dep_graph_future().ok();
+
+            result
+        })
+    }
+
+    pub fn crate_name(&self) -> Result<&Query<String>> {
+        self.crate_name.compute(|| {
+            Ok(match self.compiler.crate_name {
+                Some(ref crate_name) => crate_name.clone(),
+                None => {
+                    let parse_result = self.parse()?;
+                    let krate = parse_result.peek();
+                    find_crate_name(self.session(), &krate.attrs, &self.compiler.input)
+                }
+            })
+        })
+    }
+
+    pub fn expansion(
+        &self,
+    ) -> Result<&Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>, Lrc<LintStore>)>> {
+        tracing::trace!("expansion");
+        self.expansion.compute(|| {
+            let crate_name = self.crate_name()?.peek().clone();
+            let (krate, lint_store) = self.register_plugins()?.take();
+            let _timer = self.session().timer("configure_and_expand");
+            passes::configure_and_expand(
+                self.session().clone(),
+                lint_store.clone(),
+                self.codegen_backend().metadata_loader(),
+                krate,
+                &crate_name,
+            )
+            .map(|(krate, resolver)| {
+                (krate, Steal::new(Rc::new(RefCell::new(resolver))), lint_store)
+            })
+        })
+    }
+
+    pub fn dep_graph(&self) -> Result<&Query<DepGraph>> {
+        self.dep_graph.compute(|| {
+            Ok(match self.dep_graph_future()?.take() {
+                None => DepGraph::new_disabled(),
+                Some(future) => {
+                    let (prev_graph, prev_work_products) =
+                        self.session().time("blocked_on_dep_graph_loading", || {
+                            future
+                                .open()
+                                .unwrap_or_else(|e| rustc_incremental::LoadResult::Error {
+                                    message: format!("could not decode incremental cache: {:?}", e),
+                                })
+                                .open(self.session())
+                        });
+                    DepGraph::new(prev_graph, prev_work_products)
+                }
+            })
+        })
+    }
+
+    pub fn lower_to_hir(&'tcx self) -> Result<&Query<(&'tcx Crate<'tcx>, Steal<ResolverOutputs>)>> {
+        self.lower_to_hir.compute(|| {
+            let expansion_result = self.expansion()?;
+            let peeked = expansion_result.peek();
+            let krate = &peeked.0;
+            let resolver = peeked.1.steal();
+            let lint_store = &peeked.2;
+            let hir = resolver.borrow_mut().access(|resolver| {
+                Ok(passes::lower_to_hir(
+                    self.session(),
+                    lint_store,
+                    resolver,
+                    &*self.dep_graph()?.peek(),
+                    &krate,
+                    &self.hir_arena,
+                ))
+            })?;
+            let hir = self.hir_arena.alloc(hir);
+            Ok((hir, Steal::new(BoxedResolver::to_resolver_outputs(resolver))))
+        })
+    }
+
+    pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> {
+        self.prepare_outputs.compute(|| {
+            let expansion_result = self.expansion()?;
+            let (krate, boxed_resolver, _) = &*expansion_result.peek();
+            let crate_name = self.crate_name()?;
+            let crate_name = crate_name.peek();
+            passes::prepare_outputs(
+                self.session(),
+                self.compiler,
+                &krate,
+                &boxed_resolver,
+                &crate_name,
+            )
+        })
+    }
+
+    pub fn global_ctxt(&'tcx self) -> Result<&Query<QueryContext<'tcx>>> {
+        self.global_ctxt.compute(|| {
+            let crate_name = self.crate_name()?.peek().clone();
+            let outputs = self.prepare_outputs()?.peek().clone();
+            let lint_store = self.expansion()?.peek().2.clone();
+            let hir = self.lower_to_hir()?.peek();
+            let dep_graph = self.dep_graph()?.peek().clone();
+            let (ref krate, ref resolver_outputs) = &*hir;
+            let _timer = self.session().timer("create_global_ctxt");
+            Ok(passes::create_global_ctxt(
+                self.compiler,
+                lint_store,
+                krate,
+                dep_graph,
+                resolver_outputs.steal(),
+                outputs,
+                &crate_name,
+                &self.gcx,
+                &self.arena,
+            ))
+        })
+    }
+
+    pub fn ongoing_codegen(&'tcx self) -> Result<&Query<Box<dyn Any>>> {
+        self.ongoing_codegen.compute(|| {
+            let outputs = self.prepare_outputs()?;
+            self.global_ctxt()?.peek_mut().enter(|tcx| {
+                tcx.analysis(LOCAL_CRATE).ok();
+
+                // Don't do code generation if there were any errors
+                self.session().compile_status()?;
+
+                // Hook for compile-fail tests.
+                Self::check_for_rustc_errors_attr(tcx);
+
+                Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek()))
+            })
+        })
+    }
+
+    /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used
+    /// to write compile-fail tests that actually test that compilation succeeds without reporting
+    /// an error.
+    fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) {
+        let def_id = match tcx.entry_fn(LOCAL_CRATE) {
+            Some((def_id, _)) => def_id,
+            _ => return,
+        };
+
+        let attrs = &*tcx.get_attrs(def_id.to_def_id());
+        let attrs = attrs.iter().filter(|attr| tcx.sess.check_name(attr, sym::rustc_error));
+        for attr in attrs {
+            match attr.meta_item_list() {
+                // Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`.
+                Some(list)
+                    if list.iter().any(|list_item| {
+                        matches!(
+                            list_item.ident().map(|i| i.name),
+                            Some(sym::delay_span_bug_from_inside_query)
+                        )
+                    }) =>
+                {
+                    tcx.ensure().trigger_delay_span_bug(def_id);
+                }
+
+                // Bare `#[rustc_error]`.
+                None => {
+                    tcx.sess.span_fatal(
+                        tcx.def_span(def_id),
+                        "fatal error triggered by #[rustc_error]",
+                    );
+                }
+
+                // Some other attribute.
+                Some(_) => {
+                    tcx.sess.span_warn(
+                        tcx.def_span(def_id),
+                        "unexpected annotation used with `#[rustc_error(...)]!",
+                    );
+                }
+            }
+        }
+    }
+
+    pub fn linker(&'tcx self) -> Result<Linker> {
+        let dep_graph = self.dep_graph()?;
+        let prepare_outputs = self.prepare_outputs()?;
+        let ongoing_codegen = self.ongoing_codegen()?;
+
+        let sess = self.session().clone();
+        let codegen_backend = self.codegen_backend().clone();
+
+        Ok(Linker {
+            sess,
+            dep_graph: dep_graph.peek().clone(),
+            prepare_outputs: prepare_outputs.take(),
+            ongoing_codegen: ongoing_codegen.take(),
+            codegen_backend,
+        })
+    }
+}
+
+pub struct Linker {
+    sess: Lrc<Session>,
+    dep_graph: DepGraph,
+    prepare_outputs: OutputFilenames,
+    ongoing_codegen: Box<dyn Any>,
+    codegen_backend: Lrc<Box<dyn CodegenBackend>>,
+}
+
+impl Linker {
+    pub fn link(self) -> Result<()> {
+        let codegen_results =
+            self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess, &self.dep_graph)?;
+        let prof = self.sess.prof.clone();
+        let dep_graph = self.dep_graph;
+        prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph));
+
+        if !self
+            .sess
+            .opts
+            .output_types
+            .keys()
+            .any(|&i| i == OutputType::Exe || i == OutputType::Metadata)
+        {
+            return Ok(());
+        }
+        self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs)
+    }
+}
+
+impl Compiler {
+    pub fn enter<F, T>(&self, f: F) -> T
+    where
+        F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T,
+    {
+        let mut _timer = None;
+        let queries = Queries::new(&self);
+        let ret = f(&queries);
+
+        if self.session().opts.debugging_opts.query_stats {
+            if let Ok(gcx) = queries.global_ctxt() {
+                gcx.peek_mut().print_stats();
+            }
+        }
+
+        _timer = Some(self.session().timer("free_global_ctxt"));
+
+        ret
+    }
+}
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
new file mode 100644
index 00000000000..e94745519a4
--- /dev/null
+++ b/compiler/rustc_interface/src/tests.rs
@@ -0,0 +1,599 @@
+use crate::interface::parse_cfgspecs;
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
+use rustc_session::config::Strip;
+use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
+use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes};
+use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
+use rustc_session::config::{
+    Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion,
+};
+use rustc_session::lint::Level;
+use rustc_session::search_paths::SearchPath;
+use rustc_session::utils::NativeLibKind;
+use rustc_session::{build_session, getopts, DiagnosticOutput, Session};
+use rustc_span::edition::{Edition, DEFAULT_EDITION};
+use rustc_span::symbol::sym;
+use rustc_span::SourceFileHashAlgorithm;
+use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy};
+use rustc_target::spec::{RelocModel, RelroLevel, TlsModel};
+use std::collections::{BTreeMap, BTreeSet};
+use std::iter::FromIterator;
+use std::path::PathBuf;
+
+type CfgSpecs = FxHashSet<(String, Option<String>)>;
+
+fn build_session_options_and_crate_config(matches: getopts::Matches) -> (Options, CfgSpecs) {
+    let sessopts = build_session_options(&matches);
+    let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
+    (sessopts, cfg)
+}
+
+fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
+    let registry = registry::Registry::new(&[]);
+    let (sessopts, cfg) = build_session_options_and_crate_config(matches);
+    let sess = build_session(
+        sessopts,
+        None,
+        registry,
+        DiagnosticOutput::Default,
+        Default::default(),
+        None,
+    );
+    (sess, cfg)
+}
+
+fn new_public_extern_entry<S, I>(locations: I) -> ExternEntry
+where
+    S: Into<String>,
+    I: IntoIterator<Item = S>,
+{
+    let locations: BTreeSet<_> = locations.into_iter().map(|s| s.into()).collect();
+
+    ExternEntry {
+        location: ExternLocation::ExactPaths(locations),
+        is_private_dep: false,
+        add_prelude: true,
+    }
+}
+
+fn optgroups() -> getopts::Options {
+    let mut opts = getopts::Options::new();
+    for group in rustc_optgroups() {
+        (group.apply)(&mut opts);
+    }
+    return opts;
+}
+
+fn mk_map<K: Ord, V>(entries: Vec<(K, V)>) -> BTreeMap<K, V> {
+    BTreeMap::from_iter(entries.into_iter())
+}
+
+// When the user supplies --test we should implicitly supply --cfg test
+#[test]
+fn test_switch_implies_cfg_test() {
+    rustc_span::with_default_session_globals(|| {
+        let matches = optgroups().parse(&["--test".to_string()]).unwrap();
+        let (sess, cfg) = mk_session(matches);
+        let cfg = build_configuration(&sess, to_crate_config(cfg));
+        assert!(cfg.contains(&(sym::test, None)));
+    });
+}
+
+// When the user supplies --test and --cfg test, don't implicitly add another --cfg test
+#[test]
+fn test_switch_implies_cfg_test_unless_cfg_test() {
+    rustc_span::with_default_session_globals(|| {
+        let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap();
+        let (sess, cfg) = mk_session(matches);
+        let cfg = build_configuration(&sess, to_crate_config(cfg));
+        let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test);
+        assert!(test_items.next().is_some());
+        assert!(test_items.next().is_none());
+    });
+}
+
+#[test]
+fn test_can_print_warnings() {
+    rustc_span::with_default_session_globals(|| {
+        let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap();
+        let (sess, _) = mk_session(matches);
+        assert!(!sess.diagnostic().can_emit_warnings());
+    });
+
+    rustc_span::with_default_session_globals(|| {
+        let matches =
+            optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap();
+        let (sess, _) = mk_session(matches);
+        assert!(sess.diagnostic().can_emit_warnings());
+    });
+
+    rustc_span::with_default_session_globals(|| {
+        let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap();
+        let (sess, _) = mk_session(matches);
+        assert!(sess.diagnostic().can_emit_warnings());
+    });
+}
+
+#[test]
+fn test_output_types_tracking_hash_different_paths() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+    let mut v3 = Options::default();
+
+    v1.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]);
+    v2.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]);
+    v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
+
+    assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
+    assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash());
+    assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash());
+
+    // Check clone
+    assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+    assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+    assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+}
+
+#[test]
+fn test_output_types_tracking_hash_different_construction_order() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+
+    v1.output_types = OutputTypes::new(&[
+        (OutputType::Exe, Some(PathBuf::from("./some/thing"))),
+        (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))),
+    ]);
+
+    v2.output_types = OutputTypes::new(&[
+        (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))),
+        (OutputType::Exe, Some(PathBuf::from("./some/thing"))),
+    ]);
+
+    assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash());
+
+    // Check clone
+    assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+}
+
+#[test]
+fn test_externs_tracking_hash_different_construction_order() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+    let mut v3 = Options::default();
+
+    v1.externs = Externs::new(mk_map(vec![
+        (String::from("a"), new_public_extern_entry(vec!["b", "c"])),
+        (String::from("d"), new_public_extern_entry(vec!["e", "f"])),
+    ]));
+
+    v2.externs = Externs::new(mk_map(vec![
+        (String::from("d"), new_public_extern_entry(vec!["e", "f"])),
+        (String::from("a"), new_public_extern_entry(vec!["b", "c"])),
+    ]));
+
+    v3.externs = Externs::new(mk_map(vec![
+        (String::from("a"), new_public_extern_entry(vec!["b", "c"])),
+        (String::from("d"), new_public_extern_entry(vec!["f", "e"])),
+    ]));
+
+    assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash());
+    assert_eq!(v1.dep_tracking_hash(), v3.dep_tracking_hash());
+    assert_eq!(v2.dep_tracking_hash(), v3.dep_tracking_hash());
+
+    // Check clone
+    assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+    assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+    assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+}
+
+#[test]
+fn test_lints_tracking_hash_different_values() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+    let mut v3 = Options::default();
+
+    v1.lint_opts = vec![
+        (String::from("a"), Level::Allow),
+        (String::from("b"), Level::Warn),
+        (String::from("c"), Level::Deny),
+        (String::from("d"), Level::Forbid),
+    ];
+
+    v2.lint_opts = vec![
+        (String::from("a"), Level::Allow),
+        (String::from("b"), Level::Warn),
+        (String::from("X"), Level::Deny),
+        (String::from("d"), Level::Forbid),
+    ];
+
+    v3.lint_opts = vec![
+        (String::from("a"), Level::Allow),
+        (String::from("b"), Level::Warn),
+        (String::from("c"), Level::Forbid),
+        (String::from("d"), Level::Deny),
+    ];
+
+    assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
+    assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash());
+    assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash());
+
+    // Check clone
+    assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+    assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+    assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+}
+
+#[test]
+fn test_lints_tracking_hash_different_construction_order() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+
+    v1.lint_opts = vec![
+        (String::from("a"), Level::Allow),
+        (String::from("b"), Level::Warn),
+        (String::from("c"), Level::Deny),
+        (String::from("d"), Level::Forbid),
+    ];
+
+    v2.lint_opts = vec![
+        (String::from("a"), Level::Allow),
+        (String::from("c"), Level::Deny),
+        (String::from("b"), Level::Warn),
+        (String::from("d"), Level::Forbid),
+    ];
+
+    assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash());
+
+    // Check clone
+    assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+    assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+}
+
+#[test]
+fn test_search_paths_tracking_hash_different_order() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+    let mut v3 = Options::default();
+    let mut v4 = Options::default();
+
+    const JSON: ErrorOutputType = ErrorOutputType::Json {
+        pretty: false,
+        json_rendered: HumanReadableErrorType::Default(ColorConfig::Never),
+    };
+
+    // Reference
+    v1.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
+    v1.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
+    v1.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
+    v1.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
+    v1.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
+
+    v2.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
+    v2.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
+    v2.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
+    v2.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
+    v2.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
+
+    v3.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
+    v3.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
+    v3.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
+    v3.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
+    v3.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
+
+    v4.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
+    v4.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
+    v4.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
+    v4.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
+    v4.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
+
+    assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash());
+    assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash());
+    assert!(v1.dep_tracking_hash() == v4.dep_tracking_hash());
+
+    // Check clone
+    assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+    assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+    assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+    assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash());
+}
+
+#[test]
+fn test_native_libs_tracking_hash_different_values() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+    let mut v3 = Options::default();
+    let mut v4 = Options::default();
+
+    // Reference
+    v1.libs = vec![
+        (String::from("a"), None, NativeLibKind::StaticBundle),
+        (String::from("b"), None, NativeLibKind::Framework),
+        (String::from("c"), None, NativeLibKind::Unspecified),
+    ];
+
+    // Change label
+    v2.libs = vec![
+        (String::from("a"), None, NativeLibKind::StaticBundle),
+        (String::from("X"), None, NativeLibKind::Framework),
+        (String::from("c"), None, NativeLibKind::Unspecified),
+    ];
+
+    // Change kind
+    v3.libs = vec![
+        (String::from("a"), None, NativeLibKind::StaticBundle),
+        (String::from("b"), None, NativeLibKind::StaticBundle),
+        (String::from("c"), None, NativeLibKind::Unspecified),
+    ];
+
+    // Change new-name
+    v4.libs = vec![
+        (String::from("a"), None, NativeLibKind::StaticBundle),
+        (String::from("b"), Some(String::from("X")), NativeLibKind::Framework),
+        (String::from("c"), None, NativeLibKind::Unspecified),
+    ];
+
+    assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash());
+    assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash());
+    assert!(v1.dep_tracking_hash() != v4.dep_tracking_hash());
+
+    // Check clone
+    assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+    assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+    assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+    assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash());
+}
+
+#[test]
+fn test_native_libs_tracking_hash_different_order() {
+    let mut v1 = Options::default();
+    let mut v2 = Options::default();
+    let mut v3 = Options::default();
+
+    // Reference
+    v1.libs = vec![
+        (String::from("a"), None, NativeLibKind::StaticBundle),
+        (String::from("b"), None, NativeLibKind::Framework),
+        (String::from("c"), None, NativeLibKind::Unspecified),
+    ];
+
+    v2.libs = vec![
+        (String::from("b"), None, NativeLibKind::Framework),
+        (String::from("a"), None, NativeLibKind::StaticBundle),
+        (String::from("c"), None, NativeLibKind::Unspecified),
+    ];
+
+    v3.libs = vec![
+        (String::from("c"), None, NativeLibKind::Unspecified),
+        (String::from("a"), None, NativeLibKind::StaticBundle),
+        (String::from("b"), None, NativeLibKind::Framework),
+    ];
+
+    assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash());
+    assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash());
+    assert!(v2.dep_tracking_hash() == v3.dep_tracking_hash());
+
+    // Check clone
+    assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash());
+    assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash());
+    assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash());
+}
+
+#[test]
+fn test_codegen_options_tracking_hash() {
+    let reference = Options::default();
+    let mut opts = Options::default();
+
+    macro_rules! untracked {
+        ($name: ident, $non_default_value: expr) => {
+            opts.cg.$name = $non_default_value;
+            assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        };
+    }
+
+    // Make sure that changing an [UNTRACKED] option leaves the hash unchanged.
+    // This list is in alphabetical order.
+    untracked!(ar, String::from("abc"));
+    untracked!(codegen_units, Some(42));
+    untracked!(default_linker_libraries, true);
+    untracked!(extra_filename, String::from("extra-filename"));
+    untracked!(incremental, Some(String::from("abc")));
+    // `link_arg` is omitted because it just forwards to `link_args`.
+    untracked!(link_args, vec![String::from("abc"), String::from("def")]);
+    untracked!(link_dead_code, Some(true));
+    untracked!(linker, Some(PathBuf::from("linker")));
+    untracked!(linker_flavor, Some(LinkerFlavor::Gcc));
+    untracked!(no_stack_check, true);
+    untracked!(remark, Passes::Some(vec![String::from("pass1"), String::from("pass2")]));
+    untracked!(rpath, true);
+    untracked!(save_temps, true);
+
+    macro_rules! tracked {
+        ($name: ident, $non_default_value: expr) => {
+            opts = reference.clone();
+            opts.cg.$name = $non_default_value;
+            assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        };
+    }
+
+    // Make sure that changing a [TRACKED] option changes the hash.
+    // This list is in alphabetical order.
+    tracked!(code_model, Some(CodeModel::Large));
+    tracked!(control_flow_guard, CFGuard::Checks);
+    tracked!(debug_assertions, Some(true));
+    tracked!(debuginfo, 0xdeadbeef);
+    tracked!(embed_bitcode, false);
+    tracked!(force_frame_pointers, Some(false));
+    tracked!(force_unwind_tables, Some(true));
+    tracked!(inline_threshold, Some(0xf007ba11));
+    tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
+    tracked!(llvm_args, vec![String::from("1"), String::from("2")]);
+    tracked!(lto, LtoCli::Fat);
+    tracked!(metadata, vec![String::from("A"), String::from("B")]);
+    tracked!(no_prepopulate_passes, true);
+    tracked!(no_redzone, Some(true));
+    tracked!(no_vectorize_loops, true);
+    tracked!(no_vectorize_slp, true);
+    tracked!(opt_level, "3".to_string());
+    tracked!(overflow_checks, Some(true));
+    tracked!(panic, Some(PanicStrategy::Abort));
+    tracked!(passes, vec![String::from("1"), String::from("2")]);
+    tracked!(prefer_dynamic, true);
+    tracked!(profile_generate, SwitchWithOptPath::Enabled(None));
+    tracked!(profile_use, Some(PathBuf::from("abc")));
+    tracked!(relocation_model, Some(RelocModel::Pic));
+    tracked!(soft_float, true);
+    tracked!(target_cpu, Some(String::from("abc")));
+    tracked!(target_feature, String::from("all the features, all of them"));
+}
+
+#[test]
+fn test_debugging_options_tracking_hash() {
+    let reference = Options::default();
+    let mut opts = Options::default();
+
+    macro_rules! untracked {
+        ($name: ident, $non_default_value: expr) => {
+            opts.debugging_opts.$name = $non_default_value;
+            assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        };
+    }
+
+    // Make sure that changing an [UNTRACKED] option leaves the hash unchanged.
+    // This list is in alphabetical order.
+    untracked!(ast_json, true);
+    untracked!(ast_json_noexpand, true);
+    untracked!(borrowck, String::from("other"));
+    untracked!(borrowck_stats, true);
+    untracked!(deduplicate_diagnostics, true);
+    untracked!(dep_tasks, true);
+    untracked!(dont_buffer_diagnostics, true);
+    untracked!(dump_dep_graph, true);
+    untracked!(dump_mir, Some(String::from("abc")));
+    untracked!(dump_mir_dataflow, true);
+    untracked!(dump_mir_dir, String::from("abc"));
+    untracked!(dump_mir_exclude_pass_number, true);
+    untracked!(dump_mir_graphviz, true);
+    untracked!(emit_stack_sizes, true);
+    untracked!(hir_stats, true);
+    untracked!(identify_regions, true);
+    untracked!(incremental_ignore_spans, true);
+    untracked!(incremental_info, true);
+    untracked!(incremental_verify_ich, true);
+    untracked!(input_stats, true);
+    untracked!(keep_hygiene_data, true);
+    untracked!(link_native_libraries, false);
+    untracked!(llvm_time_trace, true);
+    untracked!(ls, true);
+    untracked!(macro_backtrace, true);
+    untracked!(meta_stats, true);
+    untracked!(nll_facts, true);
+    untracked!(no_analysis, true);
+    untracked!(no_interleave_lints, true);
+    untracked!(no_leak_check, true);
+    untracked!(no_parallel_llvm, true);
+    untracked!(parse_only, true);
+    untracked!(perf_stats, true);
+    untracked!(polonius, true);
+    // `pre_link_arg` is omitted because it just forwards to `pre_link_args`.
+    untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]);
+    untracked!(print_link_args, true);
+    untracked!(print_llvm_passes, true);
+    untracked!(print_mono_items, Some(String::from("abc")));
+    untracked!(print_type_sizes, true);
+    untracked!(query_dep_graph, true);
+    untracked!(query_stats, true);
+    untracked!(save_analysis, true);
+    untracked!(self_profile, SwitchWithOptPath::Enabled(None));
+    untracked!(self_profile_events, Some(vec![String::new()]));
+    untracked!(span_debug, true);
+    untracked!(span_free_formats, true);
+    untracked!(strip, Strip::None);
+    untracked!(terminal_width, Some(80));
+    untracked!(threads, 99);
+    untracked!(time, true);
+    untracked!(time_llvm_passes, true);
+    untracked!(time_passes, true);
+    untracked!(trace_macros, true);
+    untracked!(ui_testing, true);
+    untracked!(unpretty, Some("expanded".to_string()));
+    untracked!(unstable_options, true);
+    untracked!(validate_mir, true);
+    untracked!(verbose, true);
+
+    macro_rules! tracked {
+        ($name: ident, $non_default_value: expr) => {
+            opts = reference.clone();
+            opts.debugging_opts.$name = $non_default_value;
+            assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
+        };
+    }
+
+    // Make sure that changing a [TRACKED] option changes the hash.
+    // This list is in alphabetical order.
+    tracked!(allow_features, Some(vec![String::from("lang_items")]));
+    tracked!(always_encode_mir, true);
+    tracked!(asm_comments, true);
+    tracked!(binary_dep_depinfo, true);
+    tracked!(chalk, true);
+    tracked!(codegen_backend, Some("abc".to_string()));
+    tracked!(crate_attr, vec!["abc".to_string()]);
+    tracked!(debug_macros, true);
+    tracked!(dep_info_omit_d_target, true);
+    tracked!(dual_proc_macros, true);
+    tracked!(fewer_names, true);
+    tracked!(force_overflow_checks, Some(true));
+    tracked!(force_unstable_if_unmarked, true);
+    tracked!(fuel, Some(("abc".to_string(), 99)));
+    tracked!(human_readable_cgu_names, true);
+    tracked!(inline_in_all_cgus, Some(true));
+    tracked!(insert_sideeffect, true);
+    tracked!(instrument_coverage, true);
+    tracked!(instrument_mcount, true);
+    tracked!(link_only, true);
+    tracked!(merge_functions, Some(MergeFunctions::Disabled));
+    tracked!(mir_emit_retag, true);
+    tracked!(mir_opt_level, 3);
+    tracked!(mutable_noalias, true);
+    tracked!(new_llvm_pass_manager, true);
+    tracked!(no_codegen, true);
+    tracked!(no_generate_arange_section, true);
+    tracked!(no_link, true);
+    tracked!(no_profiler_runtime, true);
+    tracked!(osx_rpath_install_name, true);
+    tracked!(panic_abort_tests, true);
+    tracked!(plt, Some(true));
+    tracked!(print_fuel, Some("abc".to_string()));
+    tracked!(profile, true);
+    tracked!(profile_emit, Some(PathBuf::from("abc")));
+    tracked!(relro_level, Some(RelroLevel::Full));
+    tracked!(report_delayed_bugs, true);
+    tracked!(run_dsymutil, false);
+    tracked!(sanitizer, SanitizerSet::ADDRESS);
+    tracked!(sanitizer_memory_track_origins, 2);
+    tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
+    tracked!(saturating_float_casts, Some(true));
+    tracked!(share_generics, Some(true));
+    tracked!(show_span, Some(String::from("abc")));
+    tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
+    tracked!(symbol_mangling_version, SymbolManglingVersion::V0);
+    tracked!(teach, true);
+    tracked!(thinlto, Some(true));
+    tracked!(tls_model, Some(TlsModel::GeneralDynamic));
+    tracked!(treat_err_as_bug, Some(1));
+    tracked!(unleash_the_miri_inside_of_you, true);
+    tracked!(use_ctors_section, Some(true));
+    tracked!(verify_llvm_ir, true);
+}
+
+#[test]
+fn test_edition_parsing() {
+    // test default edition
+    let options = Options::default();
+    assert!(options.edition == DEFAULT_EDITION);
+
+    let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap();
+    let (sessopts, _) = build_session_options_and_crate_config(matches);
+    assert!(sessopts.edition == Edition::Edition2018)
+}
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
new file mode 100644
index 00000000000..8816ba198cf
--- /dev/null
+++ b/compiler/rustc_interface/src/util.rs
@@ -0,0 +1,770 @@
+use rustc_ast::mut_visit::{visit_clobber, MutVisitor, *};
+use rustc_ast::ptr::P;
+use rustc_ast::util::lev_distance::find_best_match_for_name;
+use rustc_ast::{self as ast, AttrVec, BlockCheckMode};
+use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::fingerprint::Fingerprint;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+#[cfg(parallel_compiler)]
+use rustc_data_structures::jobserver;
+use rustc_data_structures::stable_hasher::StableHasher;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::registry::Registry;
+use rustc_metadata::dynamic_lib::DynamicLibrary;
+use rustc_resolve::{self, Resolver};
+use rustc_session as session;
+use rustc_session::config::{self, CrateType};
+use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
+use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
+use rustc_session::parse::CrateConfig;
+use rustc_session::CrateDisambiguator;
+use rustc_session::{early_error, filesearch, output, DiagnosticOutput, Session};
+use rustc_span::edition::Edition;
+use rustc_span::source_map::FileLoader;
+use rustc_span::symbol::{sym, Symbol};
+use smallvec::SmallVec;
+use std::env;
+use std::io::{self, Write};
+use std::mem;
+use std::ops::DerefMut;
+use std::path::{Path, PathBuf};
+use std::sync::{Arc, Mutex, Once};
+#[cfg(not(parallel_compiler))]
+use std::{panic, thread};
+use tracing::info;
+
+/// Adds `target_feature = "..."` cfgs for a variety of platform
+/// specific features (SSE, NEON etc.).
+///
+/// This is performed by checking whether a set of permitted features
+/// is available on the target machine, by querying LLVM.
+pub fn add_configuration(
+    cfg: &mut CrateConfig,
+    sess: &mut Session,
+    codegen_backend: &dyn CodegenBackend,
+) {
+    let tf = sym::target_feature;
+
+    let target_features = codegen_backend.target_features(sess);
+    sess.target_features.extend(target_features.iter().cloned());
+
+    cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat))));
+
+    if sess.crt_static(None) {
+        cfg.insert((tf, Some(sym::crt_dash_static)));
+    }
+}
+
+pub fn create_session(
+    sopts: config::Options,
+    cfg: FxHashSet<(String, Option<String>)>,
+    diagnostic_output: DiagnosticOutput,
+    file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
+    input_path: Option<PathBuf>,
+    lint_caps: FxHashMap<lint::LintId, lint::Level>,
+    descriptions: Registry,
+) -> (Lrc<Session>, Lrc<Box<dyn CodegenBackend>>) {
+    let mut sess = session::build_session(
+        sopts,
+        input_path,
+        descriptions,
+        diagnostic_output,
+        lint_caps,
+        file_loader,
+    );
+
+    let codegen_backend = get_codegen_backend(&sess);
+
+    let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg));
+    add_configuration(&mut cfg, &mut sess, &*codegen_backend);
+    sess.parse_sess.config = cfg;
+
+    (Lrc::new(sess), Lrc::new(codegen_backend))
+}
+
+const STACK_SIZE: usize = 8 * 1024 * 1024;
+
+fn get_stack_size() -> Option<usize> {
+    // FIXME: Hacks on hacks. If the env is trying to override the stack size
+    // then *don't* set it explicitly.
+    env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE)
+}
+
+struct Sink(Arc<Mutex<Vec<u8>>>);
+impl Write for Sink {
+    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+        Write::write(&mut *self.0.lock().unwrap(), data)
+    }
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
+/// for `'static` bounds.
+#[cfg(not(parallel_compiler))]
+pub fn scoped_thread<F: FnOnce() -> R + Send, R: Send>(cfg: thread::Builder, f: F) -> R {
+    struct Ptr(*mut ());
+    unsafe impl Send for Ptr {}
+    unsafe impl Sync for Ptr {}
+
+    let mut f = Some(f);
+    let run = Ptr(&mut f as *mut _ as *mut ());
+    let mut result = None;
+    let result_ptr = Ptr(&mut result as *mut _ as *mut ());
+
+    let thread = cfg.spawn(move || {
+        let run = unsafe { (*(run.0 as *mut Option<F>)).take().unwrap() };
+        let result = unsafe { &mut *(result_ptr.0 as *mut Option<R>) };
+        *result = Some(run());
+    });
+
+    match thread.unwrap().join() {
+        Ok(()) => result.unwrap(),
+        Err(p) => panic::resume_unwind(p),
+    }
+}
+
+#[cfg(not(parallel_compiler))]
+pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
+    edition: Edition,
+    _threads: usize,
+    stderr: &Option<Arc<Mutex<Vec<u8>>>>,
+    f: F,
+) -> R {
+    let mut cfg = thread::Builder::new().name("rustc".to_string());
+
+    if let Some(size) = get_stack_size() {
+        cfg = cfg.stack_size(size);
+    }
+
+    crate::callbacks::setup_callbacks();
+
+    let main_handler = move || {
+        rustc_span::with_session_globals(edition, || {
+            if let Some(stderr) = stderr {
+                io::set_panic(Some(box Sink(stderr.clone())));
+            }
+            f()
+        })
+    };
+
+    scoped_thread(cfg, main_handler)
+}
+
+#[cfg(parallel_compiler)]
+pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
+    edition: Edition,
+    threads: usize,
+    stderr: &Option<Arc<Mutex<Vec<u8>>>>,
+    f: F,
+) -> R {
+    use rustc_middle::ty;
+    crate::callbacks::setup_callbacks();
+
+    let mut config = rayon::ThreadPoolBuilder::new()
+        .thread_name(|_| "rustc".to_string())
+        .acquire_thread_handler(jobserver::acquire_thread)
+        .release_thread_handler(jobserver::release_thread)
+        .num_threads(threads)
+        .deadlock_handler(|| unsafe { ty::query::handle_deadlock() });
+
+    if let Some(size) = get_stack_size() {
+        config = config.stack_size(size);
+    }
+
+    let with_pool = move |pool: &rayon::ThreadPool| pool.install(move || f());
+
+    rustc_span::with_session_globals(edition, || {
+        rustc_span::SESSION_GLOBALS.with(|session_globals| {
+            // The main handler runs for each Rayon worker thread and sets up
+            // the thread local rustc uses. `session_globals` is captured and set
+            // on the new threads.
+            let main_handler = move |thread: rayon::ThreadBuilder| {
+                rustc_span::SESSION_GLOBALS.set(session_globals, || {
+                    if let Some(stderr) = stderr {
+                        io::set_panic(Some(box Sink(stderr.clone())));
+                    }
+                    thread.run()
+                })
+            };
+
+            config.build_scoped(main_handler, with_pool).unwrap()
+        })
+    })
+}
+
+fn load_backend_from_dylib(path: &Path) -> fn() -> Box<dyn CodegenBackend> {
+    let lib = DynamicLibrary::open(path).unwrap_or_else(|err| {
+        let err = format!("couldn't load codegen backend {:?}: {:?}", path, err);
+        early_error(ErrorOutputType::default(), &err);
+    });
+    unsafe {
+        match lib.symbol("__rustc_codegen_backend") {
+            Ok(f) => {
+                mem::forget(lib);
+                mem::transmute::<*mut u8, _>(f)
+            }
+            Err(e) => {
+                let err = format!(
+                    "couldn't load codegen backend as it \
+                                   doesn't export the `__rustc_codegen_backend` \
+                                   symbol: {:?}",
+                    e
+                );
+                early_error(ErrorOutputType::default(), &err);
+            }
+        }
+    }
+}
+
+pub fn get_codegen_backend(sess: &Session) -> Box<dyn CodegenBackend> {
+    static INIT: Once = Once::new();
+
+    static mut LOAD: fn() -> Box<dyn CodegenBackend> = || unreachable!();
+
+    INIT.call_once(|| {
+        let codegen_name = sess.opts.debugging_opts.codegen_backend.as_deref().unwrap_or("llvm");
+        let backend = match codegen_name {
+            filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()),
+            codegen_name => get_builtin_codegen_backend(codegen_name),
+        };
+
+        unsafe {
+            LOAD = backend;
+        }
+    });
+    let backend = unsafe { LOAD() };
+    backend.init(sess);
+    backend
+}
+
+// This is used for rustdoc, but it uses similar machinery to codegen backend
+// loading, so we leave the code here. It is potentially useful for other tools
+// that want to invoke the rustc binary while linking to rustc as well.
+pub fn rustc_path<'a>() -> Option<&'a Path> {
+    static RUSTC_PATH: once_cell::sync::OnceCell<Option<PathBuf>> =
+        once_cell::sync::OnceCell::new();
+
+    const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR");
+
+    RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_ref().map(|v| &**v)
+}
+
+fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
+    sysroot_candidates().iter().find_map(|sysroot| {
+        let candidate = sysroot.join(bin_path).join(if cfg!(target_os = "windows") {
+            "rustc.exe"
+        } else {
+            "rustc"
+        });
+        candidate.exists().then_some(candidate)
+    })
+}
+
+fn sysroot_candidates() -> Vec<PathBuf> {
+    let target = session::config::host_triple();
+    let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()];
+    let path = current_dll_path().and_then(|s| s.canonicalize().ok());
+    if let Some(dll) = path {
+        // use `parent` twice to chop off the file name and then also the
+        // directory containing the dll which should be either `lib` or `bin`.
+        if let Some(path) = dll.parent().and_then(|p| p.parent()) {
+            // The original `path` pointed at the `rustc_driver` crate's dll.
+            // Now that dll should only be in one of two locations. The first is
+            // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
+            // other is the target's libdir, for example
+            // `$sysroot/lib/rustlib/$target/lib/*.dll`.
+            //
+            // We don't know which, so let's assume that if our `path` above
+            // ends in `$target` we *could* be in the target libdir, and always
+            // assume that we may be in the main libdir.
+            sysroot_candidates.push(path.to_owned());
+
+            if path.ends_with(target) {
+                sysroot_candidates.extend(
+                    path.parent() // chop off `$target`
+                        .and_then(|p| p.parent()) // chop off `rustlib`
+                        .and_then(|p| p.parent()) // chop off `lib`
+                        .map(|s| s.to_owned()),
+                );
+            }
+        }
+    }
+
+    return sysroot_candidates;
+
+    #[cfg(unix)]
+    fn current_dll_path() -> Option<PathBuf> {
+        use std::ffi::{CStr, OsStr};
+        use std::os::unix::prelude::*;
+
+        unsafe {
+            let addr = current_dll_path as usize as *mut _;
+            let mut info = mem::zeroed();
+            if libc::dladdr(addr, &mut info) == 0 {
+                info!("dladdr failed");
+                return None;
+            }
+            if info.dli_fname.is_null() {
+                info!("dladdr returned null pointer");
+                return None;
+            }
+            let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
+            let os = OsStr::from_bytes(bytes);
+            Some(PathBuf::from(os))
+        }
+    }
+
+    #[cfg(windows)]
+    fn current_dll_path() -> Option<PathBuf> {
+        use std::ffi::OsString;
+        use std::os::windows::prelude::*;
+        use std::ptr;
+
+        use winapi::um::libloaderapi::{
+            GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+        };
+
+        unsafe {
+            let mut module = ptr::null_mut();
+            let r = GetModuleHandleExW(
+                GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                current_dll_path as usize as *mut _,
+                &mut module,
+            );
+            if r == 0 {
+                info!("GetModuleHandleExW failed: {}", io::Error::last_os_error());
+                return None;
+            }
+            let mut space = Vec::with_capacity(1024);
+            let r = GetModuleFileNameW(module, space.as_mut_ptr(), space.capacity() as u32);
+            if r == 0 {
+                info!("GetModuleFileNameW failed: {}", io::Error::last_os_error());
+                return None;
+            }
+            let r = r as usize;
+            if r >= space.capacity() {
+                info!("our buffer was too small? {}", io::Error::last_os_error());
+                return None;
+            }
+            space.set_len(r);
+            let os = OsString::from_wide(&space);
+            Some(PathBuf::from(os))
+        }
+    }
+}
+
+pub fn get_builtin_codegen_backend(backend_name: &str) -> fn() -> Box<dyn CodegenBackend> {
+    #[cfg(feature = "llvm")]
+    {
+        if backend_name == "llvm" {
+            return rustc_codegen_llvm::LlvmCodegenBackend::new;
+        }
+    }
+
+    let err = format!("unsupported builtin codegen backend `{}`", backend_name);
+    early_error(ErrorOutputType::default(), &err);
+}
+
+pub(crate) fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator {
+    use std::hash::Hasher;
+
+    // The crate_disambiguator is a 128 bit hash. The disambiguator is fed
+    // into various other hashes quite a bit (symbol hashes, incr. comp. hashes,
+    // debuginfo type IDs, etc), so we don't want it to be too wide. 128 bits
+    // should still be safe enough to avoid collisions in practice.
+    let mut hasher = StableHasher::new();
+
+    let mut metadata = session.opts.cg.metadata.clone();
+    // We don't want the crate_disambiguator to dependent on the order
+    // -C metadata arguments, so sort them:
+    metadata.sort();
+    // Every distinct -C metadata value is only incorporated once:
+    metadata.dedup();
+
+    hasher.write(b"metadata");
+    for s in &metadata {
+        // Also incorporate the length of a metadata string, so that we generate
+        // different values for `-Cmetadata=ab -Cmetadata=c` and
+        // `-Cmetadata=a -Cmetadata=bc`
+        hasher.write_usize(s.len());
+        hasher.write(s.as_bytes());
+    }
+
+    // Also incorporate crate type, so that we don't get symbol conflicts when
+    // linking against a library of the same name, if this is an executable.
+    let is_exe = session.crate_types().contains(&CrateType::Executable);
+    hasher.write(if is_exe { b"exe" } else { b"lib" });
+
+    CrateDisambiguator::from(hasher.finish::<Fingerprint>())
+}
+
+pub(crate) fn check_attr_crate_type(
+    sess: &Session,
+    attrs: &[ast::Attribute],
+    lint_buffer: &mut LintBuffer,
+) {
+    // Unconditionally collect crate types from attributes to make them used
+    for a in attrs.iter() {
+        if sess.check_name(a, sym::crate_type) {
+            if let Some(n) = a.value_str() {
+                if categorize_crate_type(n).is_some() {
+                    return;
+                }
+
+                if let ast::MetaItemKind::NameValue(spanned) = a.meta().unwrap().kind {
+                    let span = spanned.span;
+                    let lev_candidate =
+                        find_best_match_for_name(CRATE_TYPES.iter().map(|(k, _)| k), n, None);
+                    if let Some(candidate) = lev_candidate {
+                        lint_buffer.buffer_lint_with_diagnostic(
+                            lint::builtin::UNKNOWN_CRATE_TYPES,
+                            ast::CRATE_NODE_ID,
+                            span,
+                            "invalid `crate_type` value",
+                            BuiltinLintDiagnostics::UnknownCrateTypes(
+                                span,
+                                "did you mean".to_string(),
+                                format!("\"{}\"", candidate),
+                            ),
+                        );
+                    } else {
+                        lint_buffer.buffer_lint(
+                            lint::builtin::UNKNOWN_CRATE_TYPES,
+                            ast::CRATE_NODE_ID,
+                            span,
+                            "invalid `crate_type` value",
+                        );
+                    }
+                }
+            }
+        }
+    }
+}
+
+const CRATE_TYPES: &[(Symbol, CrateType)] = &[
+    (sym::rlib, CrateType::Rlib),
+    (sym::dylib, CrateType::Dylib),
+    (sym::cdylib, CrateType::Cdylib),
+    (sym::lib, config::default_lib_output()),
+    (sym::staticlib, CrateType::Staticlib),
+    (sym::proc_dash_macro, CrateType::ProcMacro),
+    (sym::bin, CrateType::Executable),
+];
+
+fn categorize_crate_type(s: Symbol) -> Option<CrateType> {
+    Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1)
+}
+
+pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<CrateType> {
+    // Unconditionally collect crate types from attributes to make them used
+    let attr_types: Vec<CrateType> = attrs
+        .iter()
+        .filter_map(|a| {
+            if session.check_name(a, sym::crate_type) {
+                match a.value_str() {
+                    Some(s) => categorize_crate_type(s),
+                    _ => None,
+                }
+            } else {
+                None
+            }
+        })
+        .collect();
+
+    // If we're generating a test executable, then ignore all other output
+    // styles at all other locations
+    if session.opts.test {
+        return vec![CrateType::Executable];
+    }
+
+    // Only check command line flags if present. If no types are specified by
+    // command line, then reuse the empty `base` Vec to hold the types that
+    // will be found in crate attributes.
+    let mut base = session.opts.crate_types.clone();
+    if base.is_empty() {
+        base.extend(attr_types);
+        if base.is_empty() {
+            base.push(output::default_output_for_target(session));
+        } else {
+            base.sort();
+            base.dedup();
+        }
+    }
+
+    base.retain(|crate_type| {
+        let res = !output::invalid_output_for_target(session, *crate_type);
+
+        if !res {
+            session.warn(&format!(
+                "dropping unsupported crate type `{}` for target `{}`",
+                *crate_type, session.opts.target_triple
+            ));
+        }
+
+        res
+    });
+
+    base
+}
+
+pub fn build_output_filenames(
+    input: &Input,
+    odir: &Option<PathBuf>,
+    ofile: &Option<PathBuf>,
+    attrs: &[ast::Attribute],
+    sess: &Session,
+) -> OutputFilenames {
+    match *ofile {
+        None => {
+            // "-" as input file will cause the parser to read from stdin so we
+            // have to make up a name
+            // We want to toss everything after the final '.'
+            let dirpath = (*odir).as_ref().cloned().unwrap_or_default();
+
+            // If a crate name is present, we use it as the link name
+            let stem = sess
+                .opts
+                .crate_name
+                .clone()
+                .or_else(|| rustc_attr::find_crate_name(&sess, attrs).map(|n| n.to_string()))
+                .unwrap_or_else(|| input.filestem().to_owned());
+
+            OutputFilenames::new(
+                dirpath,
+                stem,
+                None,
+                sess.opts.cg.extra_filename.clone(),
+                sess.opts.output_types.clone(),
+            )
+        }
+
+        Some(ref out_file) => {
+            let unnamed_output_types =
+                sess.opts.output_types.values().filter(|a| a.is_none()).count();
+            let ofile = if unnamed_output_types > 1 {
+                sess.warn(
+                    "due to multiple output types requested, the explicitly specified \
+                     output file name will be adapted for each output type",
+                );
+                None
+            } else {
+                if !sess.opts.cg.extra_filename.is_empty() {
+                    sess.warn("ignoring -C extra-filename flag due to -o flag");
+                }
+                Some(out_file.clone())
+            };
+            if *odir != None {
+                sess.warn("ignoring --out-dir flag due to -o flag");
+            }
+
+            OutputFilenames::new(
+                out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
+                out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(),
+                ofile,
+                sess.opts.cg.extra_filename.clone(),
+                sess.opts.output_types.clone(),
+            )
+        }
+    }
+}
+
+// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere.
+//
+// FIXME: Currently the `everybody_loops` transformation is not applied to:
+//  * `const fn`, due to issue #43636 that `loop` is not supported for const evaluation. We are
+//    waiting for miri to fix that.
+//  * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging.
+//    Solving this may require `!` to implement every trait, which relies on the an even more
+//    ambitious form of the closed RFC #1637. See also [#34511].
+//
+// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401
+pub struct ReplaceBodyWithLoop<'a, 'b> {
+    within_static_or_const: bool,
+    nested_blocks: Option<Vec<ast::Block>>,
+    resolver: &'a mut Resolver<'b>,
+}
+
+impl<'a, 'b> ReplaceBodyWithLoop<'a, 'b> {
+    pub fn new(resolver: &'a mut Resolver<'b>) -> ReplaceBodyWithLoop<'a, 'b> {
+        ReplaceBodyWithLoop { within_static_or_const: false, nested_blocks: None, resolver }
+    }
+
+    fn run<R, F: FnOnce(&mut Self) -> R>(&mut self, is_const: bool, action: F) -> R {
+        let old_const = mem::replace(&mut self.within_static_or_const, is_const);
+        let old_blocks = self.nested_blocks.take();
+        let ret = action(self);
+        self.within_static_or_const = old_const;
+        self.nested_blocks = old_blocks;
+        ret
+    }
+
+    fn should_ignore_fn(ret_ty: &ast::FnRetTy) -> bool {
+        if let ast::FnRetTy::Ty(ref ty) = ret_ty {
+            fn involves_impl_trait(ty: &ast::Ty) -> bool {
+                match ty.kind {
+                    ast::TyKind::ImplTrait(..) => true,
+                    ast::TyKind::Slice(ref subty)
+                    | ast::TyKind::Array(ref subty, _)
+                    | ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. })
+                    | ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. })
+                    | ast::TyKind::Paren(ref subty) => involves_impl_trait(subty),
+                    ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()),
+                    ast::TyKind::Path(_, ref path) => {
+                        path.segments.iter().any(|seg| match seg.args.as_deref() {
+                            None => false,
+                            Some(&ast::GenericArgs::AngleBracketed(ref data)) => {
+                                data.args.iter().any(|arg| match arg {
+                                    ast::AngleBracketedArg::Arg(arg) => match arg {
+                                        ast::GenericArg::Type(ty) => involves_impl_trait(ty),
+                                        ast::GenericArg::Lifetime(_)
+                                        | ast::GenericArg::Const(_) => false,
+                                    },
+                                    ast::AngleBracketedArg::Constraint(c) => match c.kind {
+                                        ast::AssocTyConstraintKind::Bound { .. } => true,
+                                        ast::AssocTyConstraintKind::Equality { ref ty } => {
+                                            involves_impl_trait(ty)
+                                        }
+                                    },
+                                })
+                            }
+                            Some(&ast::GenericArgs::Parenthesized(ref data)) => {
+                                any_involves_impl_trait(data.inputs.iter())
+                                    || ReplaceBodyWithLoop::should_ignore_fn(&data.output)
+                            }
+                        })
+                    }
+                    _ => false,
+                }
+            }
+
+            fn any_involves_impl_trait<'a, I: Iterator<Item = &'a P<ast::Ty>>>(mut it: I) -> bool {
+                it.any(|subty| involves_impl_trait(subty))
+            }
+
+            involves_impl_trait(ty)
+        } else {
+            false
+        }
+    }
+
+    fn is_sig_const(sig: &ast::FnSig) -> bool {
+        matches!(sig.header.constness, ast::Const::Yes(_))
+            || ReplaceBodyWithLoop::should_ignore_fn(&sig.decl.output)
+    }
+}
+
+impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
+    fn visit_item_kind(&mut self, i: &mut ast::ItemKind) {
+        let is_const = match i {
+            ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true,
+            ast::ItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig),
+            _ => false,
+        };
+        self.run(is_const, |s| noop_visit_item_kind(i, s))
+    }
+
+    fn flat_map_trait_item(&mut self, i: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
+        let is_const = match i.kind {
+            ast::AssocItemKind::Const(..) => true,
+            ast::AssocItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig),
+            _ => false,
+        };
+        self.run(is_const, |s| noop_flat_map_assoc_item(i, s))
+    }
+
+    fn flat_map_impl_item(&mut self, i: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
+        self.flat_map_trait_item(i)
+    }
+
+    fn visit_anon_const(&mut self, c: &mut ast::AnonConst) {
+        self.run(true, |s| noop_visit_anon_const(c, s))
+    }
+
+    fn visit_block(&mut self, b: &mut P<ast::Block>) {
+        fn stmt_to_block(
+            rules: ast::BlockCheckMode,
+            s: Option<ast::Stmt>,
+            resolver: &mut Resolver<'_>,
+        ) -> ast::Block {
+            ast::Block {
+                stmts: s.into_iter().collect(),
+                rules,
+                id: resolver.next_node_id(),
+                span: rustc_span::DUMMY_SP,
+            }
+        }
+
+        fn block_to_stmt(b: ast::Block, resolver: &mut Resolver<'_>) -> ast::Stmt {
+            let expr = P(ast::Expr {
+                id: resolver.next_node_id(),
+                kind: ast::ExprKind::Block(P(b), None),
+                span: rustc_span::DUMMY_SP,
+                attrs: AttrVec::new(),
+                tokens: None,
+            });
+
+            ast::Stmt {
+                id: resolver.next_node_id(),
+                kind: ast::StmtKind::Expr(expr),
+                span: rustc_span::DUMMY_SP,
+            }
+        }
+
+        let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.resolver);
+        let loop_expr = P(ast::Expr {
+            kind: ast::ExprKind::Loop(P(empty_block), None),
+            id: self.resolver.next_node_id(),
+            span: rustc_span::DUMMY_SP,
+            attrs: AttrVec::new(),
+            tokens: None,
+        });
+
+        let loop_stmt = ast::Stmt {
+            id: self.resolver.next_node_id(),
+            span: rustc_span::DUMMY_SP,
+            kind: ast::StmtKind::Expr(loop_expr),
+        };
+
+        if self.within_static_or_const {
+            noop_visit_block(b, self)
+        } else {
+            visit_clobber(b.deref_mut(), |b| {
+                let mut stmts = vec![];
+                for s in b.stmts {
+                    let old_blocks = self.nested_blocks.replace(vec![]);
+
+                    stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item()));
+
+                    // we put a Some in there earlier with that replace(), so this is valid
+                    let new_blocks = self.nested_blocks.take().unwrap();
+                    self.nested_blocks = old_blocks;
+                    stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, self.resolver)));
+                }
+
+                let mut new_block = ast::Block { stmts, ..b };
+
+                if let Some(old_blocks) = self.nested_blocks.as_mut() {
+                    //push our fresh block onto the cache and yield an empty block with `loop {}`
+                    if !new_block.stmts.is_empty() {
+                        old_blocks.push(new_block);
+                    }
+
+                    stmt_to_block(b.rules, Some(loop_stmt), &mut self.resolver)
+                } else {
+                    //push `loop {}` onto the end of our fresh block and yield that
+                    new_block.stmts.push(loop_stmt);
+
+                    new_block
+                }
+            })
+        }
+    }
+
+    // in general the pretty printer processes unexpanded code, so
+    // we override the default `visit_mac` method which panics.
+    fn visit_mac(&mut self, mac: &mut ast::MacCall) {
+        noop_visit_mac(mac, self)
+    }
+}