The rustc-dev-guide

repository·main·Indexed 23 days ago

https://github.com/rust-lang/rustc-dev-guide

A collaborative resource explaining the internal workings of the Rust compiler (rustc). It serves as an orientation tool for new contributors and a reference for experienced developers, covering high-level compiler architecture, source code representation, analysis, MIR to binaries, and the process of building and debugging rustc.

Tokens
184.6K
Snippets
321
Records
979
Agent score
83%

What's inside rustc-dev-guide

  1. Overview of the rustc-dev-guide

    main

    The rustc-dev-guide is a comprehensive resource designed to document the inner workings of rustc (the Rust compiler) and to assist new contributors in the development process. The guide is organized into several key areas:

    • Building and debugging rustc: Instructions for building, debugging, and profiling the compiler.
    • Contributing to Rust: Procedures for contribution, using git/GitHub, and stabilizing features.
    • Bootstrapping: How the compiler builds itself using previous versions.
    • High-level compiler architecture: The stages of the compilation process.
    • Source code representation: How raw source code is transformed into intermediate forms.
    • Supporting infrastructure: Command-line argument conventions, compiler entry points (like rustc_driver and rustc_interface), and the design of errors and lints.
    • Analysis: How the compiler checks code properties (e.g., type checking).
    • MIR to binaries: The process of generating linked executable machine code.
    • Appendices: Reference information and a glossary.
  2. The stages of the AST transformation pipeline

    main

    The conversion of source code into a validated AST involves the following key processes:

    1. Lexing and Parsing: Converting raw text into a structured format.
    2. Macro Expansion: Expanding macros, which may require parsing the resulting output.
    3. Name Resolution: Resolving names of macros and imports (required by macro expansion).
    4. Conditional Compilation: Handling #[cfg] attributes and similar logic.
    5. Feature-gate Checking: Ensuring the code uses allowed language features.
    6. AST Validation: Final verification of the AST structure.
  3. Understand the rustdoc component structure

    main

    The rustdoc tool is split into several parts:

    • librustdoc: Contains the bulk of the logic.
    • src/tools/rustdoc: The binary that calls rustdoc::main.
    • src/tools/rustdoc-js and src/tools/rustdoc-themes: JavaScript and CSS for documentation rendering.
    • src/rustdoc-json-types: Type definitions used when generating output with --output-format=json.
  4. Understand the rust-lang/rust workspace structure

    main

    The rust-lang/rust repository is a single large Cargo workspace. It is organized into three main directories that house the core components of the Rust ecosystem:

    • compiler/: Contains the source code for rustc, organized into many interdependent crates.
    • library/: Contains the standard libraries (core, alloc, std, proc_macro, test) and the Rust runtime (backtrace, rtstartup, lang_start).
    • tests/: Contains the compiler tests.
    • src/: Contains source code for tools like rustdoc, clippy, cargo, the build system, and language documentation.
  5. Understand the invariants of the Rust type system

    main

    The Rust type system relies on several invariants—guarantees that certain properties remain true at all times. When developing for or with the type system, you must distinguish between invariants that currently hold (✅) and those that do not (❌).

    Warning: Do not rely on invariants marked with ❌ for soundness, as they are either fundamentally broken or unlikely to be fixed. These include:

    • Applying inference results from a goal does not change its result: Re-evaluating a goal after applying its resulting inference constraints may yield a different result.
    • The type system is complete: The solver may fail to prove a goal that actually holds (e.g., in method selection or opaque type inference).
    • Removing ambiguity makes strictly more things compile: Due to incompleteness, improving inference can cause breaking changes in existing code.

    Invariants marked with ✅ mostly hold, though some have known bugs or exceptions (e.g., wf(X) implies wf(normalize(X)) is currently broken by issue #84533).

  6. What is monomorphization in Rust

    main

    Monomorphization is the process where the Rust compiler generates a unique copy of a generic function for every concrete type it is used with. This ensures high performance by allowing the compiler to work with specific types rather than pointers to heap-allocated objects (as seen in languages like Java), but it increases compile times and binary size due to the creation of multiple code copies.

    For example, using both Vec<u64> and Vec<String> results in two distinct copies of the Vec implementation in the final binary.

  7. What is Chalk-based trait solving?

    main

    Chalk is an experimental trait solver for Rust developed by the Types team. It aims to enable advanced trait system features and bug fixes that are difficult to implement in the current solver, such as Generic Associated Types (GATs) and specialization.

    Conceptually, Chalk recasts the Rust trait system as a logic program. It "lowers" Rust code (structs, traits, and impl declarations) into logical inference rules, which are then executed via queries, similar to how a Prolog solver operates. While it uses a more expressive variant than standard Prolog Horn clauses, the underlying mechanism is based on logical inference.

  8. What is Salsa and how does it work?

    main

    Salsa is a library for incremental recomputation. It allows reusing previous computations to increase efficiency by automatically tracking dependencies between values.

    Core Mechanism

    1. Base Inputs: Values provided directly to the system (e.g., source files, manifests) that are not computed by Salsa.
    2. Derived Values: Intermediate values produced by 'pure' functions. Salsa tracks which inputs were accessed to compute these values.
    3. Dependency Graph: Salsa treats computations as a graph of nodes. When an input changes, Salsa only recomputes downstream values until it finds a node that remains unchanged (early termination), preventing unnecessary work.

    This model is used extensively in rust-analyzer and chalk.

  9. What is an eRFC (Experimental RFC)?

    main

    An eRFC is a variant of the RFC process used for highly complex features where the high-level need is clear, but the detailed specification is too large to settle on upfront.

    Instead of a final design, an eRFC outlines a high-level strategy to authorize a period of active experimentation. This allows developers to implement the feature behind a feature gate to gather practical data, which then informs a subsequent formal RFC for stabilization.

    While used for major features like coroutines, the explicit "eRFC" label is rarely used today; the project now generally prefers approving a standard RFC for an initial version and iterating via the nightly channel.

  10. What is canonicalization in the trait solver?

    main

    Canonicalization is the process of isolating an inference value from its context. It is used to implement [canonical queries][cq] to improve caching and detect cycles during trait resolution.

    In practice, this involves replacing unbound inference variables (and free lifetimes) with canonical placeholders (e.g., ?0, ?1, ?2) in a consistent, fixed order. This ensures that two different queries that are structurally identical (even if they use different original inference variables) result in the same canonical form. For example, if ?T and ?U are distinct unbound variables, the type (?T, ?U) and (?U, ?T) both canonicalize to (?0, ?1).

  11. What is `ErrorGuaranteed` and how is it used?

    main

    ErrorGuaranteed is a zero-sized type used within the compiler to provide a static guarantee that a compilation will fail. It is generated by the rustc_errors crate whenever an error is reported to the user (via the emit() function).

    Key Characteristics

    • Static Failure Guarantee: If your code encounters a value of type ErrorGuaranteed, the compilation is guaranteed to fail.
    • Unconstructable: It cannot be constructed outside of the rustc_errors crate, preventing manual creation of error states.
    • Purpose: It is primarily used to prevent unsoundness bugs by allowing developers to statically verify that error-handling code paths actually lead to a compilation failure.

    Usage Constraints

    • No Error Metadata: It does not convey the kind of error that occurred. Do not use it to decide whether to emit an error or to determine what type of error to emit.
    • Post-Emission Only: It indicates that an error has already been emitted. You cannot use ErrorGuaranteed to signal that a future part of the compiler will error; you must first call emit() to generate the ErrorGuaranteed token.