|
| 1 | +# Sharing the trait solver with rust-analyzer |
| 2 | + |
| 3 | +rust-analyzer can be viewed as a compiler frontend: it performs tasks similar to the parts of rustc |
| 4 | +that run before code generation, such as parsing, lexing, AST construction and lowering, HIR |
| 5 | +lowering, and even limited MIR building and const evaluation. |
| 6 | + |
| 7 | +However, because rust-analyzer is primarily a language server, its architecture differs in several |
| 8 | +important ways from that of rustc. |
| 9 | +Despite these differences, a substantial portion of its responsibilities—most notably type |
| 10 | +inference and trait solving—overlap with the compiler. |
| 11 | + |
| 12 | +To avoid duplication and to maintain consistency between the two implementations, rust-analyzer |
| 13 | +reuses several crates from rustc, relying on shared abstractions wherever possible. |
| 14 | + |
| 15 | +## Shared Crates |
| 16 | + |
| 17 | +Currently, rust-analyzer depends on several `rustc_*` crates from the compiler: |
| 18 | + |
| 19 | +- `rustc_abi` |
| 20 | +- `rustc_ast_ir` |
| 21 | +- `rustc_index` |
| 22 | +- `rustc_lexer` |
| 23 | +- `rustc_next_trait_solver` |
| 24 | +- `rustc_parse_format` |
| 25 | +- `rustc_pattern_analysis` |
| 26 | +- `rustc_type_ir` |
| 27 | + |
| 28 | +Since these crates are not published on `crates.io` as part of the compiler's normal distribution |
| 29 | +process, rust-analyzer maintains its own publishing pipeline. |
| 30 | +It uses the [rustc-auto-publish script][rustc-auto-publish] to publish these crates to `crates.io` |
| 31 | +with the prefix `ra-ap-rustc_*` |
| 32 | +(for example: https://crates.io/crates/ra-ap-rustc_next_trait_solver). |
| 33 | +rust-analyzer then depends on these re-published crates in its own build. |
| 34 | + |
| 35 | +For trait solving specifically, the primary shared crates are `rustc_type_ir` and |
| 36 | +`rustc_next_trait_solver`, which provide the core IR and solver logic used by both compiler |
| 37 | +frontends. |
| 38 | + |
| 39 | +## The Abstraction Layer |
| 40 | + |
| 41 | +Because rust-analyzer is a language server, it must handle frequently changing source code and |
| 42 | +partially invalid or incomplete source codes. |
| 43 | +This requires an infrastructure quite different from rustc's, especially in the layers between |
| 44 | +the source code and the HIR—for example, `Ty` and its backing interner. |
| 45 | + |
| 46 | +To bridge these differences, the compiler provides `rustc_type_ir` as an abstraction layer shared |
| 47 | +between rustc and rust-analyzer. |
| 48 | +This crate defines the fundamental interfaces used to represent types, predicates, and the context |
| 49 | +required by the trait solver. |
| 50 | +Both rustc and rust-analyzer implement these traits for their own concrete type representations, |
| 51 | +and `rustc_next_trait_solver` is written to be generic over these abstractions. |
| 52 | + |
| 53 | +In addition to these interfaces, `rustc_type_ir` also includes several non-trivial components built |
| 54 | +on top of the abstraction layer—such as elaboration logic and the search graph machinery used by the |
| 55 | +solver. |
| 56 | + |
| 57 | +## Design Concepts |
| 58 | + |
| 59 | +`rustc_next_trait_solver` is intended to depend only on the abstract interfaces defined in |
| 60 | +`rustc_type_ir`. |
| 61 | +To support this, the type-system traits in `rustc_type_ir` must expose every interface the solver |
| 62 | +requires—for example, [creating a new inference type variable][ir new_infer] |
| 63 | +([rustc][rustc new_infer], [rust-analyzer][r-a new_infer]). |
| 64 | +For items that do not need compiler-specific representations, `rustc_type_ir` defines them directly |
| 65 | +as structs or enums parameterized over these traits—for example, [`TraitRef`][ir tr]. |
| 66 | + |
| 67 | +The following are some notable items from the `rustc_type_ir` crate. |
| 68 | + |
| 69 | +### `trait Interner` |
| 70 | + |
| 71 | +The central trait in this design is [`Interner`][ir interner], which specifies all |
| 72 | +implementation-specific details for both rustc and rust-analyzer. |
| 73 | +Among its essential responsibilities: |
| 74 | + |
| 75 | +- it **specifies** the concrete types used by the implementation via its |
| 76 | + [associated types][ir interner assocs]; these form the backbone of how each compiler frontend |
| 77 | + instantiates the shared IR, |
| 78 | +- it provides the context required by the solver (e.g., querying [lang items][ir require_lang_item], |
| 79 | + enumerating [all blanket impls for a trait][ir for_each_blanket_impl]); |
| 80 | +- and it must implement [`IrPrint`][ir irprint] for formatting and tracing. |
| 81 | + In practice, these `IrPrint` impls simply route to existing formatting logic inside rustc or |
| 82 | + rust-analyzer. |
| 83 | + |
| 84 | +In rustc, [`TyCtxt` implements `Interner`][rustc interner impl]: it exposes the rustc's query |
| 85 | +methods, and the required `Interner` trait methods are implemented by invoking those queries. |
| 86 | +In rust-analyzer, the implementing type is named [`DbInterner`][r-a interner impl] (as it performs |
| 87 | +most interning through the [salsa] database), and most of its methods are backed by salsa queries |
| 88 | +rather than rustc queries. |
| 89 | + |
| 90 | +### `mod inherent` |
| 91 | + |
| 92 | +Another notable item in `rustc_type_ir` is the [`inherent` module][ir inherent]. |
| 93 | +This module provides *forward definitions* of inherent methods—expressed as traits—corresponding to |
| 94 | +methods that exist on compiler-specific types such as `Ty` or `GenericArg`. |
| 95 | +These definitions allow the generic crates (such as `rustc_next_trait_solver`) to call methods that |
| 96 | +are implemented differently in rustc and rust-analyzer. |
| 97 | + |
| 98 | +Code in generic crates should import these definitions with: |
| 99 | + |
| 100 | +```rust |
| 101 | +use inherent::*; |
| 102 | +``` |
| 103 | + |
| 104 | +These forward definitions **must never be used inside the concrete implementations themselves**. |
| 105 | +Crates that implement the traits from `mod inherent` should call the actual inherent methods on |
| 106 | +their concrete types once those types are nameable. |
| 107 | + |
| 108 | +You can find rustc’s implementations of these traits in the |
| 109 | +[rustc_middle::ty::inherent][rustc inherent impl] module. |
| 110 | +For rust-analyzer, the corresponding implementations are located across several modules under |
| 111 | +`hir_ty::next_solver`, such as [hir_ty::next_solver::region][r-a inherent impl]. |
| 112 | + |
| 113 | +### `trait InferCtxtLike` and `trait SolverDelegate` |
| 114 | + |
| 115 | +These two traits correspond to the role of [`InferCtxt`][rustc inferctxt] in rustc. |
| 116 | + |
| 117 | +[`InferCtxtLike`][ir inferctxtlike] must be defined in `rustc_infer` due to coherence |
| 118 | +constraints(orphan rules). |
| 119 | +As a result, it cannot provide functionality that lives in `rustc_trait_selection`. |
| 120 | +Instead, behavior that depends on trait-solving logic is abstracted into a separate trait, |
| 121 | +[`SolverDelegate`][ir solverdelegate]. |
| 122 | +Its implementator in rustc is [simply a newtype struct over `InferCtxt`][rustc solverdelegate impl] |
| 123 | +in `rustc_trait_selection`. |
| 124 | + |
| 125 | +(In rust-analyzer, it is also implemented for a newtype wrapper over its own |
| 126 | +[`InferCtxt`][r-a inferctxtlike impl], primarily to mirror rustc’s structure, although this is not |
| 127 | +strictly necessary because all solver-related logic already resides in the `hir-ty` crate.) |
| 128 | + |
| 129 | +In the long term, the ideal design is to move all of the logic currently expressed through |
| 130 | +`SolverDelegate` into `rustc_next_trait_solver`, with any required core operations added directly to |
| 131 | +`InferCtxtLike`. |
| 132 | +This would allow more of the solver’s behavior to live entirely inside the shared solver crate. |
| 133 | + |
| 134 | +### `rustc_type_ir::search_graph::{Cx, Delegate}` |
| 135 | + |
| 136 | +The abstraction traits [`Cx`][ir searchgraph cx impl] and [`Delegate`][ir searchgraph delegate impl] |
| 137 | +are already implemented within `rustc_next_trait_solver` itself. |
| 138 | +Therefore, users of the shared crates—both rustc and rust-analyzer—do not need to provide their own |
| 139 | +implementations. |
| 140 | + |
| 141 | +These traits exist primarily to support fuzzing of the search graph independently of the full trait |
| 142 | +solver. |
| 143 | +This infrastructure is used by the external fuzzing project: |
| 144 | +<https://github.com/lcnr/search_graph_fuzz>. |
| 145 | + |
| 146 | +## Long-term plans for supporting rust-analyzer |
| 147 | + |
| 148 | +In general, we aim to support rust-analyzer just as well as rustc in these shared crates—provided |
| 149 | +doing so does not substantially harm rustc's performance or maintainability. |
| 150 | +(e.g., [#145377][pr 145377], [#146111][pr 146111], [#146182][pr 146182] and [#147723][pr 147723]) |
| 151 | + |
| 152 | +Shared crates that require nightly-only features must guard such code behind a `nightly` feature |
| 153 | +flag, since rust-analyzer is built with the stable toolchain. |
| 154 | + |
| 155 | +Looking forward, we plan to uplift more shared logic into `rustc_type_ir`. |
| 156 | +There are still duplicated implementations between rustc and rust-analyzer—such as `ObligationCtxt` |
| 157 | +([rustc][rustc oblctxt], [rust-analyzer][r-a oblctxt]) and type coercion logic |
| 158 | +([rustc][rustc coerce], [rust-analyzer][r-a coerce])—that we would like to unify over time. |
| 159 | + |
| 160 | +[rustc-auto-publish]: https://github.com/rust-analyzer/rustc-auto-publish |
| 161 | +[ir new_infer]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/inherent/trait.Ty.html#tymethod.new_infer |
| 162 | +[rustc new_infer]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_middle/src/ty/sty.rs#L413-L420 |
| 163 | +[r-a new_infer]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/next_solver/ty.rs#L59-L92 |
| 164 | +[ir tr]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/struct.TraitRef.html |
| 165 | +[ir interner]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html |
| 166 | +[ir interner assocs]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html#required-associated-types |
| 167 | +[ir require_lang_item]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html#tymethod.require_lang_item |
| 168 | +[ir for_each_blanket_impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html#tymethod.for_each_blanket_impl |
| 169 | +[ir irprint]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/ir_print/trait.IrPrint.html |
| 170 | +[rustc interner impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#impl-Interner-for-TyCtxt%3C'tcx%3E |
| 171 | +[r-a interner impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/interner.rs |
| 172 | +[salsa]: https://github.com/salsa-rs/salsa |
| 173 | +[ir inherent]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/inherent/index.html |
| 174 | +[rustc inherent impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_middle/ty/inherent/index.html |
| 175 | +[r-a inherent impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/region.rs |
| 176 | +[ir inferctxtlike]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.InferCtxtLike.html |
| 177 | +[rustc inferctxt]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html |
| 178 | +[rustc inferctxtlike impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/src/rustc_infer/infer/context.rs.html#14-332 |
| 179 | +[r-a inferctxtlike impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/infer/context.rs |
| 180 | +[ir solverdelegate]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_next_trait_solver/delegate/trait.SolverDelegate.html |
| 181 | +[rustc solverdelegate impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_trait_selection/solve/delegate/struct.SolverDelegate.html |
| 182 | +[r-a solverdelegate impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/solver.rs#L27-L330 |
| 183 | +[ir searchgraph cx impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/src/rustc_type_ir/interner.rs.html#550-575 |
| 184 | +[ir searchgraph delegate impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/src/rustc_next_trait_solver/solve/search_graph.rs.html#20-123 |
| 185 | +[pr 145377]: https://github.com/rust-lang/rust/pull/145377 |
| 186 | +[pr 146111]: https://github.com/rust-lang/rust/pull/146111 |
| 187 | +[pr 146182]: https://github.com/rust-lang/rust/pull/146182 |
| 188 | +[pr 147723]: https://github.com/rust-lang/rust/pull/147723 |
| 189 | +[rustc oblctxt]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_trait_selection/src/traits/engine.rs#L48-L386 |
| 190 | +[r-a oblctxt]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/next_solver/obligation_ctxt.rs |
| 191 | +[rustc coerce]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_hir_typeck/src/coercion.rs |
| 192 | +[r-a coerce]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/infer/coerce.rs |
0 commit comments