Open-Source Wikis

/

Rust

/

Compiler

/

Driver and interface

rust-lang/rust

Driver and interface

The driver is the part of rustc that turns "user invokes rustc foo.rs" into "a sequence of compiler phases is executed and produces output." It lives across three crates plus rustc_session.

Purpose

The compiler driver's job is:

  1. Parse command-line arguments (and embedded args from response files / RUSTFLAGS)
  2. Set up the Session, the diagnostic context, the source map, and the target
  3. Drive each compilation phase in order, querying through rustc_interface
  4. Handle the special --print … modes (--print=cfg, --print=target-list, etc.) without running a full compile
  5. Catch panics and route them to a useful ICE report

Most actual compilation logic lives in queries in rustc_middle and the per-phase crates; the driver wires them together.

Key crates

File Purpose
compiler/rustc/src/main.rs The 40-line main; calls rustc_driver::main() and configures jemalloc
compiler/rustc_driver/src/lib.rs Public driver API; thin re-exports
compiler/rustc_driver_impl/src/lib.rs The 65 KB implementation: argument parsing, phase dispatch, --print modes, ICE reporting, signal handling
compiler/rustc_driver_impl/src/args.rs Argument file expansion (@file.txt)
compiler/rustc_driver_impl/src/pretty.rs The --unpretty / -Zunpretty modes
compiler/rustc_driver_impl/src/signal_handler.rs SIGSEGV / stack-overflow handling
compiler/rustc_interface/src/lib.rs The "run a compilation" facade
compiler/rustc_session/src/lib.rs Session, parse options, target

Why split rustc_driver and rustc_driver_impl?

The split is for compile-time and dynamic-linking reasons. rustc_driver is a tiny crate that the rustc binary depends on; rustc_driver_impl is the heavy implementation. Re-exporting through a thin facade makes the dependency graph cleaner and keeps the rustc binary's compile time low. The same pattern shows up with rustc_query_impl and is documented in the rustc-dev-guide.

High-level flow

sequenceDiagram
    participant User
    participant Main as rustc::main
    participant Driver as rustc_driver_impl
    participant Interface as rustc_interface
    participant Queries as TyCtxt + queries
    participant Codegen as codegen backend

    User->>Main: rustc foo.rs
    Main->>Driver: run_compiler(args, &mut callbacks)
    Driver->>Driver: parse args, build Session, install hooks
    Driver->>Interface: rustc_interface::run_compiler(config, |compiler| { … })
    Interface->>Queries: tcx.analysis(())
    Queries->>Queries: parse → expand → resolve → lower → typeck → borrowck → mir-opt
    Queries-->>Codegen: tcx.collect_and_partition_mono_items
    Codegen->>Codegen: emit object files
    Codegen->>Driver: link
    Driver-->>User: exit code

rustc_interface is the layer that turns "a compilation" into a closure that runs in a thread with a fresh TyCtxt. The Compiler and Queries structs there expose phase-by-phase access (parse → expansion → analysis → ongoing codegen) for tools that want to stop early.

Argument processing

Argument processing has more layers than you might expect:

  1. The shell hands argv to main.
  2. args::arg_expand_all expands @response-files and merges in RUSTFLAGS and CARGO_ENCODED_RUSTFLAGS.
  3. The clap-like parser in rustc_session turns the args into a Session::opts (config::Options).
  4. Some flags need a Session already constructed (target spec, edition); those are processed in a second pass after Session::new.

The full option schema lives in compiler/rustc_session/src/options.rs (one of the bigger files in the compiler).

--print modes

Many --print=KIND flags do not run the full compiler — they just look at the session and exit:

Print kind Source
--print=cfg session's cfg list
--print=target-list hard-coded list in rustc_target
--print=target-spec-json Target::to_json on the selected target
--print=sysroot Session::sysroot
--print=crate-name parses just enough of the crate root to extract #![crate_name]
--print=file-names computes output filenames (no codegen)
--print=link-args dry-run the linker invocation

The list is in rustc_driver_impl::print_crate_info and friends.

ICE handling

When rustc panics, the panic hook installed by rustc_driver produces:

  • The source location of the panic
  • The query stack at the time of the panic (each running query pushes onto an ImplicitCtxt)
  • An invitation to file a bug, with a path to a fully-detailed rustc-ice-…txt report
  • (On nightly) a backtrace if RUST_BACKTRACE is set

The query stack is what makes rustc panics legible; without it you'd see "panic in analyze_mir" with no idea what the user wrote that caused it.

Tools that embed rustc

Anything that wants to be a "compiler that does X first" embeds rustc through rustc_driver and replaces the callbacks:

  • rustdoc — same compiler frontend, different post-analysis backend
  • clippy — registers extra lints, then defers to the normal driver
  • miri — runs MIR through its interpreter instead of codegen
  • rust-analyzer — does not embed rustc; it has its own analysis (different design choice)

Each of these gets stage 1 rustc as a build dependency and is compiled with the matching nightly. See Tools.

Entry points for modification

  • Adding a -Z flag → compiler/rustc_session/src/options.rs
  • Adding a --print=… mode → print_crate_info in rustc_driver_impl
  • Changing how phases are sequenced → rustc_interface
  • Changing the panic hook / ICE message → rustc_driver_impl::install_ice_hook
  • Hooking compilation from a custom binary → implement Callbacks in rustc_driver and call rustc_driver::RunCompiler::new(...).set_callbacks(...).run()

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Driver and interface – Rust wiki | Factory