Skip to content

Commit 888d29e

Browse files
committed
Mention sharing the solver with rust-analyzer
1 parent d3a11b5 commit 888d29e

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@
185185
- [Proof trees](./solve/proof-trees.md)
186186
- [Opaque types](./solve/opaque-types.md)
187187
- [Significant changes and quirks](./solve/significant-changes.md)
188+
- [Sharing the trait solver with rust-analyzer](./solve/sharing-crates-with-rust-analyzer.md)
188189
- [`Unsize` and `CoerceUnsized` traits](./traits/unsize.md)
189190
- [Variance](./variance.md)
190191
- [Coherence checking](./coherence.md)
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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 [`Interner`][r-a interner impl], and most of its
87+
methods are backed by [salsa] queries rather than rustc queries.
88+
89+
### `mod inherent`
90+
91+
Another notable item in `rustc_type_ir` is the [`inherent` module][ir inherent].
92+
This module provides *forward definitions* of inherent methods—expressed as traits—corresponding to
93+
methods that exist on compiler-specific types such as `Ty` or `GenericArg`.
94+
These definitions allow the generic crates (such as `rustc_next_trait_solver`) to call methods that
95+
are implemented differently in rustc and rust-analyzer.
96+
97+
Code in generic crates should import these definitions with:
98+
99+
```rust
100+
use inherent::*;
101+
```
102+
103+
These forward definitions **must never be used inside the concrete implementations themselves**.
104+
Crates that implement the traits from `mod inherent` should call the actual inherent methods on
105+
their concrete types once those types are nameable.
106+
107+
You can find rustc’s implementations of these traits in the
108+
[rustc_middle::ty::inherent][rustc inherent impl] module.
109+
For rust-analyzer, the corresponding implementations are located across several modules under
110+
`hir_ty::next_solver`, such as [hir_ty::next_solver::region][r-a inherent impl].
111+
112+
### `trait InferCtxtLike` and `trait SolverDelegate`
113+
114+
These two traits correspond to the role of [`InferCtxt`][rustc inferctxt] in rustc.
115+
116+
[`InferCtxtLike`][ir inferctxtlike] must be defined in `rustc_infer` due to coherence
117+
constraints(orphan rules).
118+
As a result, it cannot provide functionality that lives in `rustc_trait_selection`.
119+
Instead, behavior that depends on trait-solving logic is abstracted into a separate trait,
120+
[`SolverDelegate`][ir solverdelegate].
121+
Its implementator in rustc is [simply a newtype struct over `InferCtxt`][rustc solverdelegate impl]
122+
in `rustc_trait_selection`.
123+
124+
(In rust-analyzer, it is also implemented for a newtype wrapper over its own
125+
[`InferCtxt`][r-a inferctxtlike impl], primarily to mirror rustc’s structure, although this is not
126+
strictly necessary because all solver-related logic already resides in the `hir-ty` crate.)
127+
128+
In the long term, the ideal design is to move all of the logic currently expressed through
129+
`SolverDelegate` into `rustc_next_trait_solver`, with any required core operations added directly to
130+
`InferCtxtLike`.
131+
This would allow more of the solver’s behavior to live entirely inside the shared solver crate.
132+
133+
### `rustc_type_ir::search_graph::{Cx, Delegate}`
134+
135+
### `rustc_type_ir::search_graph::{Cx, Delegate}`
136+
137+
The abstraction traits [`Cx`][ir searchgraph cx impl] and [`Delegate`][ir searchgraph delegate impl]
138+
are already implemented within `rustc_next_trait_solver` itself.
139+
Therefore, users of the shared crates—both rustc and rust-analyzer—do not need to provide their own
140+
implementations.
141+
142+
These traits exist primarily to support fuzzing of the search graph independently of the full trait
143+
solver.
144+
This infrastructure is used by the external fuzzing project:
145+
<https://github.com/lcnr/search_graph_fuzz>.
146+
147+
## Long-term plans for supporting rust-analyzer
148+
149+
In general, we aim to support rust-analyzer just as well as rustc in these shared crates—provided
150+
doing so does not substantially harm rustc's performance or maintainability.
151+
(e.g., [#145377][pr 145377], [#146111][pr 146111], [#146182][pr 146182] and [#147723][pr 147723])
152+
153+
Shared crates that require nightly-only features must guard such code behind a `nightly` feature
154+
flag, since rust-analyzer is built with the stable toolchain.
155+
156+
Looking forward, we plan to uplift more shared logic into `rustc_type_ir`.
157+
There are still duplicated implementations between rustc and rust-analyzer—such as `ObligationCtxt`
158+
([rustc][rustc oblctxt], [rust-analyzer][r-a oblctxt]) and type coercion logic
159+
([rustc][rustc coerce], [rust-analyzer][r-a coerce])—that we would like to unify over time.
160+
161+
[rustc-auto-publish]: https://github.com/rust-analyzer/rustc-auto-publish
162+
[ir new_infer]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/inherent/trait.Ty.html#tymethod.new_infer
163+
[rustc new_infer]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_middle/src/ty/sty.rs#L413-L420
164+
[r-a new_infer]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/next_solver/ty.rs#L59-L92
165+
[ir tr]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/struct.TraitRef.html
166+
[ir interner]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html
167+
[ir interner assocs]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html#required-associated-types
168+
[ir require_lang_item]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html#tymethod.require_lang_item
169+
[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
170+
[ir irprint]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/ir_print/trait.IrPrint.html
171+
[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
172+
[r-a interner impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/interner.rs
173+
[salsa]: https://github.com/salsa-rs/salsa
174+
[ir inherent]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/inherent/index.html
175+
[rustc inherent impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_middle/ty/inherent/index.html
176+
[r-a inherent impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/region.rs
177+
[ir inferctxtlike]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.InferCtxtLike.html
178+
[rustc inferctxt]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html
179+
[rustc inferctxtlike impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/src/rustc_infer/infer/context.rs.html#14-332
180+
[r-a inferctxtlike impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/infer/context.rs
181+
[ir solverdelegate]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_next_trait_solver/delegate/trait.SolverDelegate.html
182+
[rustc solverdelegate impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_trait_selection/solve/delegate/struct.SolverDelegate.html
183+
[r-a solverdelegate impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/solver.rs#L27-L330
184+
[ir searchgraph cx impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/src/rustc_type_ir/interner.rs.html#550-575
185+
[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
186+
[pr 145377]: https://github.com/rust-lang/rust/pull/145377
187+
[pr 146111]: https://github.com/rust-lang/rust/pull/146111
188+
[pr 146182]: https://github.com/rust-lang/rust/pull/146182
189+
[pr 147723]: https://github.com/rust-lang/rust/pull/147723
190+
[rustc oblctxt]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_trait_selection/src/traits/engine.rs#L48-L386
191+
[r-a oblctxt]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/next_solver/obligation_ctxt.rs
192+
[rustc coerce]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_hir_typeck/src/coercion.rs
193+
[r-a coerce]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/infer/coerce.rs

0 commit comments

Comments
 (0)