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:
- Parse command-line arguments (and embedded args from response files /
RUSTFLAGS) - Set up the
Session, the diagnostic context, the source map, and the target - Drive each compilation phase in order, querying through
rustc_interface - Handle the special
--print …modes (--print=cfg,--print=target-list, etc.) without running a full compile - 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 coderustc_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:
- The shell hands argv to
main. args::arg_expand_allexpands@response-filesand merges inRUSTFLAGSandCARGO_ENCODED_RUSTFLAGS.- The clap-like parser in
rustc_sessionturns the args into aSession::opts(config::Options). - Some flags need a
Sessionalready constructed (target spec, edition); those are processed in a second pass afterSession::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-…txtreport - (On nightly) a backtrace if
RUST_BACKTRACEis 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
-Zflag →compiler/rustc_session/src/options.rs - Adding a
--print=…mode →print_crate_infoinrustc_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
Callbacksinrustc_driverand callrustc_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.