You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rollup merge of #140643 - makai410:smir-refactor-migrate, r=oli-obk,celinval
Refactor StableMIR
This PR refactors stable-mir according to the guidance in [this doc](https://hackmd.io/jBRkZLqAQL2EVgwIIeNMHg). It reverses the dependency between `rustc_smir` and `stable_mir`, making `rustc_smir` completely agnostic of `stable_mir`.
Under the new architecture, the `rustc_smir` crate would retain direct access to rustc queries, while `stable_mir` should proxy all such requests through `rustc_smir` instead of accessing rustc's internals directly. `stable_mir` would only be responsible for the conversion between internal and stable constructs.
This PR mainly introduces these changes:
- **Bridge / Tables<'tcx, B: Bridge>**
```rust
/// A trait defining types that are used to emulate StableMIR components, which is really
/// useful when programming in stable_mir-agnostic settings.
pub trait Bridge {
type DefId: Copy + Debug + PartialEq + IndexedVal;
type AllocId: Copy + Debug + PartialEq + IndexedVal;
type Span: Copy + Debug + PartialEq + IndexedVal;
type Ty: Copy + Debug + PartialEq + IndexedVal;
type InstanceDef: Copy + Debug + PartialEq + IndexedVal;
type TyConstId: Copy + Debug + PartialEq + IndexedVal;
type MirConstId: Copy + Debug + PartialEq + IndexedVal;
type Layout: Copy + Debug + PartialEq + IndexedVal;
type Error: SmirError;
}
pub struct Tables<'tcx, B: Bridge> {
tcx: TyCtxt<'tcx>,
pub(crate) def_ids: IndexMap<DefId, B::DefId>,
pub(crate) alloc_ids: IndexMap<AllocId, B::AllocId>,
pub(crate) spans: IndexMap<rustc_span::Span, B::Span>,
pub(crate) types: IndexMap<Ty<'tcx>, B::Ty>,
pub(crate) instances: IndexMap<ty::Instance<'tcx>, B::InstanceDef>,
pub(crate) ty_consts: IndexMap<ty::Const<'tcx>, B::TyConstId>,
pub(crate) mir_consts: IndexMap<mir::Const<'tcx>, B::MirConstId>,
pub(crate) layouts: IndexMap<rustc_abi::Layout<'tcx>, B::Layout>,
}
```
Since `rustc_smir` needs these stable types somewhere, using associated types is a good approach.
- **SmirContainer / SmirInterface**
```rust
/// A container which is used for TLS.
pub struct SmirContainer<'tcx, B: Bridge> {
pub tables: RefCell<Tables<'tcx, B>>,
pub cx: RefCell<SmirCtxt<'tcx, B>>,
}
impl<'tcx> SmirInterface for SmirContainer<'tcx, BridgeTys> {
// ...
}
/// Provides direct access to rustc's internal queries.
///
/// The [`crate::stable_mir::compiler_interface::SmirInterface`] must go through
/// this context to obtain rustc-level information.
pub struct SmirCtxt<'tcx, B: Bridge> {
tcx: TyCtxt<'tcx>,
_marker: PhantomData<B>,
}
```
This PR moves `Tables` from `SmirCtxt` to a new `SmirContainer` struct, since mutable borrows of `tables` should only be managed by `SmirInterface`. This change prevents `SmirCtxt` from holding separate borrows and requires passing `tables` explicitly when needed:
```rust
impl<'tcx, B: Bridge> SmirCtxt<'tcx, B> {
// ...
/// Get the body of an Instance which is already monomorphized.
pub fn instance_body(
&self,
instance: ty::Instance<'tcx>,
tables: &mut Tables<'tcx, B>,
) -> Option<Body<'tcx>> {
tables
.instance_has_body(instance)
.then(|| BodyBuilder::new(self.tcx, instance).build(tables))
}
// ...
}
```
This PR introduces `SmirContainer` as a separate struct rather than bundling it into a `SmirInterface` struct. This separation makes the architecture more modular and easier to reason about.
- **context/traits.rs**
We use this file to define traits that are used for encapsulating the associated functions in the rustc's internals. This is much easier to use and maintain than directly cramming everything into `SmirCtxt`. Here is a real-world use case:
```rust
impl RustcInternal for ExistentialTraitRef {
type T<'tcx> = rustc_ty::ExistentialTraitRef<'tcx>;
fn internal<'tcx>(
&self,
tables: &mut Tables<'_, BridgeTys>,
cx: &SmirCtxt<'tcx, BridgeTys>,
) -> Self::T<'tcx> {
use rustc_smir::context::SmirExistentialTraitRef;
cx.new_from_args(self.def_id.0.internal(tables, cx), self.generic_args.internal(tables, cx))
}
}
```
- **Separation of `rustc_smir::alloc`**
The previous `rustc_smir::alloc` had many direct calls to rustc queries. This PR splits it into two parts: `rustc_smir::alloc` and `stable_mir::alloc`. Following the same pattern as `SmirCtxt` and `SmirInterface`, the `rustc_smir::alloc` handles all direct interactions with rustc queries and performs the actual memory allocations, while the `stable_mir::alloc` is responsible for constructing stable components.
- **Removal of `convert/error.rs`**
We use `SmirError::from_internal` instead, since implementing `Stable` for these internal errors would be redundant—`tables` is not actually used. If we later need to add something like `LayoutError` to `stable_mir`, we could implement it as follows:
```rust
impl SmirError for stable_mir::LayoutError {
fn from_internal<T: Debug>(err: T) -> Self {
// ...
}
}
```
**Unresolved questions:**
- There are still a few direct calls to rustc's internals scattered across `impl Stable`s, but most of them appear to be relatively stable, e.g., `mir::interpret::ConstAllocation::inner(self)` and `mir::syntax::SwitchTargets::otherwise(self)`.
r? `@celinval`
0 commit comments