Skip to content

Commit d650c1e

Browse files
authored
Highway to Compat (AC⚡DC rustc edition) (#123)
This PR builds on #121 (the pipeline refactor) with two goals: make it structurally easy to absorb rustc API changes, and remove the manual bookkeeping around which rustc commit our tests need. This also addresses Issue #90 and supersedes PR #91 (see "Why this supersedes #91" below). ## The compat layer (`src/compat/`) All usage of rustc internal APIs (`rustc_middle`, `rustc_smir`, `rustc_span`, etc.) is now routed through a single module. When rustc changes its internals (which it does regularly), fixes concentrate in `compat/` rather than scattering across the codebase. The design decision, boundary contract, and validation results are documented in [ADR-003](docs/adr/003-compat-layer-for-rustc-internals.md). The module is organized by concern: | Submodule | What it wraps | |-----------|---------------| | `bridge.rs` | Stable-to-internal conversions (`Instance`, `InstanceKind`, unevaluated consts), plus the lifetime-free `OpaqueInstanceKind` | | `mono_collect.rs` | `tcx.collect_and_partition_mono_items()` and symbol name resolution | | `output.rs` | Output filename resolution from the compiler session | | `spans.rs` | `span_to_location_info()` and `FileNameDisplayPreference` | | `types.rs` | Type queries: generics, signatures, discriminants, attributes | | `mod.rs` | Crate re-exports (`rustc_middle`, `rustc_smir`, `stable_mir`, vendored `serde`) and common type aliases (`TyCtxt`, `DefId`) | The compat layer deliberately does *not* abstract over stable MIR's own public API. When `stable_mir` changes its types (e.g. `Rvalue::AddressOf` gaining a new variant), any consumer has to adapt; that's expected. The boundary is: rustc implementation details go through compat; the stable MIR contract flows through directly. This also fixes a gap in `mk_graph/`, which was using `extern crate stable_mir` directly (bypassing the abstraction). Those imports now go through `compat` like everything else. A few thin compat wrappers that were just one-line pass-throughs got inlined at the call site; the compat layer wraps unstable *boundaries*, not every function call. ## Single source of truth for the rustc commit This PR adds a `[metadata]` section to `rust-toolchain.toml` with the `rustc-commit` that backs our pinned nightly: ```toml [metadata] rustc-commit = "a2545fd6fc66b4323f555223a860c451885d1d2b" ``` But that created a gap: now the commit hash lives in `rust-toolchain.toml` *and* is still hardcoded in `.github/workflows/test.yml` (the `actions/checkout` ref for `rust-lang/rust`). Two sources of truth, one of which will inevitably drift. The fix for this is to make the TOML the single source of truth and have everything else read from it. (rustup silently ignores unknown keys, so the `[metadata]` section is safe; it's purely for our scripts and CI.) ### What changes **CI (`test.yml`)** The `ui-tests` job now installs `yq` (via `mikefarah/yq-action`), reads the commit hash from `rust-toolchain.toml`, and passes it to `actions/checkout` for `rust-lang/rust`. No more hardcoded hash in the workflow. If the metadata key is missing or empty, the step fails loudly with a non-zero exit. **Local test scripts (`run_ui_tests.sh`, `remake_ui_tests.sh`)** Both scripts now source a shared helper (`ensure_rustc_commit.sh`) that: 1. Reads `metadata.rustc-commit` from `rust-toolchain.toml` using `yq` 2. Detects whether `RUST_DIR_ROOT` points to a regular clone or a bare repo 3. For a regular clone: runs `git checkout <commit>` (or skips if already there) 4. For a bare repo: creates a detached worktree at `$RUST_DIR/<short-hash>/` (or reuses it if one already exists at the right commit) This means you no longer have to manually check out the right commit before running `make test-ui` or `make remake-ui-tests`. Just point `RUST_DIR_ROOT` at your rust checkout and the scripts handle the rest. `yq` (the Go version, [mikefarah/yq](https://github.com/mikefarah/yq)) is now required for UI tests. If it's missing, the script tells you how to install it and exits. ### A note on worktrees for the rust checkout Rust's repo is enormous. When you inevitably have to switch between commits (toolchain bumps, bisecting, etc.), `git checkout` in a regular clone rewrites hundreds of thousands of files, blows away build caches, and generally makes you wait while incremental builds start over from scratch. Not great. A bare repo with worktrees avoids all of this. Each worktree shares the same object store but keeps its own working tree and build artifacts. Switching between commits is just a `cd`; no files get rewritten, no caches get invalidated. If you've already built at one commit, you can hop to another worktree and come back without losing anything. To convert an existing rust clone to bare+worktree: ```bash cd .. mv rust rust-tmp git clone --bare git@github.com:rust-lang/rust.git rust cd rust # Create a worktree for the commit our tests need git worktree add ./a2545fd6fc66 a2545fd6fc66b4323f555223a860c451885d1d2b --detach ``` Then point the test scripts at the repo root: ```bash RUST_DIR_ROOT=~/oss/rust make test-ui ``` The scripts detect the bare repo and create (or reuse) the worktree automatically. When the toolchain bumps to a new nightly, a new worktree gets created at the new commit; the old one sticks around and you can prune it whenever you feel like it. ## Stress test validation To verify the abstraction actually holds, we ran two toolchain bumps on ephemeral spike branches (since deleted): **6-month jump** (nightly-2024-11-29 to nightly-2025-06-01, rustc 1.85 to 1.89) and **13-month jump** (nightly-2024-11-29 to nightly-2026-01-15, rustc 1.85 to 1.94). All rustc internal API changes were fully contained in `compat/` and `driver.rs`: | Change | Where it was absorbed | |--------|----------------------| | `collect_and_partition_mono_items` tuple to `MonoItemPartitions` struct | `compat/mono_collect.rs` | | `RunCompiler::new().run()` becoming `run_compiler()` | `driver.rs` | | `stable_mir` renamed to `rustc_public` | `compat/mod.rs` (one-line alias) | | `rustc_smir` renamed to `rustc_public_bridge` | `compat/mod.rs`, `driver.rs` | | `FileNameDisplayPreference::Remapped` removed | `compat/spans.rs` | Nothing leaked into `printer/` or `mk_graph/`. Changes that *did* affect `printer/` and `mk_graph/` were all stable MIR public API evolution (the kind of thing any consumer would need to handle regardless): - `Rvalue::AddressOf` changed from `Mutability` to `RawPtrKind` - `StatementKind::Deinit` and `Rvalue::NullaryOp` removed from stable MIR - `AggregateKind::CoroutineClosure` added (async closures) - `Coroutine` and `Dynamic` lost a field each - `PointerCoercion::ReifyFnPointer` gained a `Safety` parameter - `GlobalAlloc::TypeId` added - `Ty::visit()` return type changed from `()` to `ControlFlow<T>` The 13-month bump also exposed the cost of the original `mk_graph/` gap: when `stable_mir` was renamed to `rustc_public`, all 5 mk_graph files needed `extern crate rustc_public as stable_mir`, while `printer/` needed zero import changes because it already went through compat. This PR closes that gap; a future rename would be a single-line change in `compat/mod.rs`. ## Why this supersedes #91 PR #91 and this PR both address Issue #90 (upgrading the rustc dependency to a recent nightly), but they take fundamentally different approaches. The core difference: this PR creates an adapter API (`src/compat/`) that gates all rustc access through a single module, so that API churn is absorbed in one place. PR #91 patches every call site across the codebase directly. When rustc changes its internal APIs, the cost of adapting depends on how many files touch those APIs. In the codebase as it existed when #91 was written, rustc internals were scattered everywhere: `printer.rs`, `mk_graph/`, `driver.rs`, and various helpers all had their own `extern crate` declarations and direct imports. Every file that directly calls a rustc API is a file that breaks when that API changes. PR #91 updates the toolchain from `nightly-2024-11-29` to `nightly-2025-07-20` and fixes every call site that broke. The result works, but the changes scatter across `printer.rs`, `mk_graph.rs`, `driver.rs`, `Cargo.toml`, the jq filter, and every golden test file. The next toolchain bump would require the same scattershot patching, because nothing has changed about how the codebase interfaces with rustc. This PR invests in the adapter layer first. To make the difference concrete, here's what the `stable_mir` to `rustc_public` crate rename looks like in each approach: **PR #91's approach**: update every file that declares `extern crate stable_mir` or imports from it: `printer.rs`, all 5 `mk_graph/` files, and `driver.rs`. **This PR's approach**: change one line in `compat/mod.rs`: ```rust // before pub extern crate stable_mir; // after pub extern crate rustc_public as stable_mir; ``` Every consumer imports via `use crate::compat::stable_mir`, so nothing else changes. This PR doesn't bump the toolchain yet. It lays the groundwork so that the bump, when it lands, is a contained change in `compat/` and `driver.rs` rather than a codebase-wide patch. PR #91 should be closed once this merges; the upgrade itself can follow as a focused PR that only touches the compat layer. ## Test plan - [ ] `cargo build` and `cargo clippy` pass - [ ] `make integration-test` passes - [ ] `make test-ui` passes (requires `RUST_DIR_ROOT`) - [ ] Verify no `extern crate rustc_*` or `extern crate stable_mir` declarations remain outside `src/compat/` and `src/driver.rs` ## Reviewer ask: try a toolchain bump The best way to evaluate this PR is to try bumping the nightly toolchain and see where breakage lands. Change the channel in `rust-toolchain.toml` to a newer nightly date, run `cargo build`, and check whether the compiler errors are confined to `src/compat/` and `src/driver.rs`. If something breaks in `printer/` or `mk_graph/`, that's either a compat layer gap we should fix before merging, or a stable MIR public API change (which is expected and out of scope for this abstraction).
1 parent 6d02243 commit d650c1e

30 files changed

+675
-309
lines changed

.github/workflows/test.yml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,34 @@ jobs:
9999
ref: ${{ github.event.pull_request.head.sha }}
100100
submodules: recursive
101101

102+
- name: Install yq
103+
run: |
104+
set -euo pipefail
105+
YQ_VERSION="v4.52.4"
106+
mkdir -p "$HOME/.local/bin"
107+
wget -qO "$HOME/.local/bin/yq" \
108+
"https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64"
109+
chmod +x "$HOME/.local/bin/yq"
110+
echo "$HOME/.local/bin" >> $GITHUB_PATH
111+
yq --version
112+
113+
114+
- name: 'Read rustc commit from rust-toolchain.toml'
115+
id: rustc-meta
116+
run: |
117+
set -euo pipefail
118+
COMMIT=$(yq '.metadata.rustc-commit' rust-toolchain.toml)
119+
if [ -z "$COMMIT" ] || [ "$COMMIT" = "null" ]; then
120+
echo "::error::metadata.rustc-commit not found in rust-toolchain.toml"
121+
exit 1
122+
fi
123+
echo "rustc-commit=$COMMIT" >> "$GITHUB_OUTPUT"
124+
102125
- name: 'Check out Rust repo'
103126
uses: actions/checkout@v4
104127
with:
105128
repository: rust-lang/rust
106-
ref: a2545fd6fc66b4323f555223a860c451885d1d2b # hash of Hardcoded Rust version
129+
ref: ${{ steps.rustc-meta.outputs.rustc-commit }}
107130
path: rust
108131
fetch-depth: 1
109132

@@ -126,7 +149,7 @@ jobs:
126149
strategy:
127150
fail-fast: false
128151
matrix:
129-
runner: [normal, MacM1] # MacM1 / normal are self-hosted,
152+
runner: [normal, MacM1] # MacM1 / normal are self-hosted,
130153
runs-on: ${{ matrix.runner }}
131154
timeout-minutes: 20
132155
steps:

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
77
Note: this changelog was introduced at 0.2.0. The 0.1.0 section is a
88
retroactive best-effort summary; earlier changes were not formally tracked.
99

10+
## [Unreleased]
11+
12+
### Added
13+
- `src/compat/` module isolating all rustc internal API usage behind a stable boundary; printer/ now has zero `extern crate rustc_*` declarations and zero direct `tcx.query()` calls
14+
- `OpaqueInstanceKind` owned type replacing `middle::ty::InstanceKind<'tcx>`, eliminating the `'tcx` lifetime parameter from `SmirJson`, `LinkMapKey`, `FnSymInfo`, `LinkMap`, `DerivedInfo`, and `SmirJsonDebugInfo`
15+
- `metadata.rustc-commit` field in `rust-toolchain.toml` as single source of truth for the rustc commit used by UI tests
16+
- `ensure_rustc_commit.sh` helper that reads the expected commit from `rust-toolchain.toml` (via `yq`) and ensures the rust checkout (regular or bare+worktree) is at that commit; CI installs `yq` on PATH to support this
17+
- ADR-003 documenting compat layer design decisions and validation results from two toolchain bump stress tests (6-month and 13-month jumps)
18+
19+
### Changed
20+
- Routed `mk_graph/` stable_mir imports through the compat module
21+
- Eliminated thin compat wrappers in printer/ (`mono_collect`, `mono_item_name`, `has_attr`, `def_id_to_inst`, `GenericData` newtype, `SourceData` alias); callers now go through the compat boundary directly
22+
- UI test scripts (`run_ui_tests.sh`, `remake_ui_tests.sh`) now source `ensure_rustc_commit.sh` and use `RUST_SRC_DIR` instead of using the raw directory argument directly
23+
1024
## [0.2.0] - 2026-02-21
1125

1226
### Added
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# ADR-003: Compatibility layer for rustc internal APIs
2+
3+
**Status:** Accepted
4+
**Date:** 2026-03-02
5+
6+
## Context
7+
8+
stable-mir-json hooks into rustc's internal APIs (`rustc_middle`, `rustc_smir`, `rustc_span`, etc.) to extract MIR data. These APIs are unstable; they change regularly across nightly releases, and the crate names themselves get renamed (the `stable_mir` crate became `rustc_public`, `rustc_smir` became `rustc_public_bridge`, etc.). Before this decision, rustc internals were used directly throughout the codebase: `printer.rs`, `mk_graph/`, `driver.rs`, and various helpers all had their own `extern crate` declarations and direct imports. So a toolchain bump meant hunting through every file that touched a changed API; not fun, and easy to miss things.
9+
10+
## Decision
11+
12+
Route all rustc internal API usage through a single `src/compat/` module. The module re-exports crate names (so a rename like `stable_mir` to `rustc_public` is a one-line alias change in `compat/mod.rs`) and wraps unstable functions behind stable signatures (so a changed calling convention is absorbed in one place).
13+
14+
The compat layer does *not* try to abstract over stable MIR's own public API. When `stable_mir` (the public, downstream-facing API) changes its types, any consumer has to adapt; that's by design. The boundary is: if it's a rustc implementation detail, it goes through compat; if it's the stable MIR contract, it flows through directly.
15+
16+
`src/driver.rs` is the one exception; it uses `rustc_driver` and `rustc_interface` directly because it *is* the rustc integration point. Everything else goes through compat.
17+
18+
## Consequences
19+
20+
**What the compat layer absorbs (rustc internals):**
21+
22+
The table below shows changes observed during validation (see the Validation section) and where each was contained. Note that `driver.rs` changes are listed here because they stay within the rustc integration boundary, even though `driver.rs` sits outside `compat/` itself.
23+
24+
| Change | Absorbed in |
25+
|--------|-------------|
26+
| `collect_and_partition_mono_items` tuple to `MonoItemPartitions` struct | `compat/mono_collect.rs` |
27+
| `RunCompiler::new().run()` becoming `run_compiler()` | `driver.rs` |
28+
| `stable_mir` renamed to `rustc_public` | `compat/mod.rs` (re-exported as alias) |
29+
| `rustc_smir` renamed to `rustc_public_bridge` | `compat/mod.rs`, `driver.rs` |
30+
| `FileNameDisplayPreference` variants changing | `compat/spans.rs` |
31+
32+
None of these changes leaked into `printer/` or `mk_graph/`. The abstraction worked as designed.
33+
34+
**What still propagates (stable MIR public API evolution):**
35+
36+
- `Rvalue::AddressOf` changed from `Mutability` to `RawPtrKind`
37+
- `StatementKind::Deinit` and `Rvalue::NullaryOp` removed
38+
- `AggregateKind::CoroutineClosure` added
39+
- `Coroutine` and `Dynamic` field count changes
40+
- `Ty::visit()` return type changed from `()` to `ControlFlow<T>`
41+
42+
These affect `printer/` and `mk_graph/` regardless of the compat layer. Any consumer of stable MIR would need to handle them; there's nothing we can (or should) do about that.
43+
44+
**The mk_graph gap (now fixed).** The `mk_graph/` files originally declared their own `extern crate stable_mir`, bypassing the abstraction entirely. This was introduced in commit `e9395d9` (PR #111) before the compat layer existed; it wasn't an oversight so much as a timing issue. The 13-month toolchain bump exposed the cost: when `stable_mir` was renamed to `rustc_public`, all 5 mk_graph files needed updating, while `printer/` needed zero import path changes because it already went through compat. This branch closes the gap by routing all mk_graph imports through `use crate::compat::stable_mir`.
45+
46+
## Validation
47+
48+
We stress-tested the abstraction against two toolchain bumps on ephemeral branches (branched off `spike/hex-rustc`, since deleted) to see if it actually holds up in practice:
49+
50+
- **6-month jump** (nightly-2024-11-29 to nightly-2025-06-01, rustc 1.85 to 1.89): all internal API changes contained in `compat/` and `driver.rs`
51+
- **13-month jump** (nightly-2024-11-29 to nightly-2026-01-15, rustc 1.85 to 1.94): same containment, plus the major `stable_mir` to `rustc_public` crate rename absorbed by a single alias in `compat/mod.rs`
52+
53+
The validation branches were disposable spike work and have been removed. Detailed findings are recorded in the PR description for this branch.

rust-toolchain.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
[toolchain]
22
channel = "nightly-2024-11-29"
33
components = ["llvm-tools", "rustc-dev", "rust-src", "rust-analyzer"]
4+
5+
# Ignored by rustup; used by our test scripts.
6+
# This is the rustc commit that backs the nightly above.
7+
# UI test scripts automatically checkout this commit in RUST_DIR_ROOT.
8+
[metadata]
9+
rustc-commit = "a2545fd6fc66b4323f555223a860c451885d1d2b"

src/compat/bridge.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! Stable<->internal conversions and OpaqueInstanceKind.
2+
//!
3+
//! This module wraps rustc-internal instance kind queries behind an owned,
4+
//! lifetime-free representation so that the rest of the codebase doesn't
5+
//! need to carry `'tcx` lifetimes for link map keys.
6+
7+
use std::hash::{Hash, Hasher};
8+
9+
use super::middle;
10+
use super::rustc_internal;
11+
use super::stable_mir;
12+
use super::TyCtxt;
13+
use stable_mir::mir::mono::Instance;
14+
15+
/// Owned, lifetime-free replacement for `middle::ty::InstanceKind<'tcx>`.
16+
///
17+
/// The actual `InstanceKind` usage is narrow:
18+
/// 1. Serialized as `format!("{:?}", kind)` (a Debug string)
19+
/// 2. Checked via `is_reify_shim()` (a single pattern match)
20+
/// 3. Used for `Hash`/`Eq` in `LinkMapKey` (map keying)
21+
///
22+
/// This struct captures all three via owned data, eliminating the need
23+
/// to propagate the `'tcx` lifetime through `LinkMapKey`, `FnSymInfo`,
24+
/// `SmirJson`, and `SmirJsonDebugInfo`.
25+
#[derive(Clone, Debug)]
26+
pub struct OpaqueInstanceKind {
27+
debug_repr: String,
28+
pub is_reify_shim: bool,
29+
}
30+
31+
impl PartialEq for OpaqueInstanceKind {
32+
fn eq(&self, other: &Self) -> bool {
33+
self.debug_repr == other.debug_repr
34+
}
35+
}
36+
37+
impl Eq for OpaqueInstanceKind {}
38+
39+
impl Hash for OpaqueInstanceKind {
40+
fn hash<H: Hasher>(&self, state: &mut H) {
41+
self.debug_repr.hash(state);
42+
}
43+
}
44+
45+
impl std::fmt::Display for OpaqueInstanceKind {
46+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47+
write!(f, "{}", self.debug_repr)
48+
}
49+
}
50+
51+
/// Create a monomorphized Instance from a stable DefId (wraps `Instance::mono`).
52+
pub fn mono_instance(tcx: TyCtxt<'_>, id: stable_mir::DefId) -> Instance {
53+
let internal_id = rustc_internal::internal(tcx, id);
54+
let internal_inst = middle::ty::Instance::mono(tcx, internal_id);
55+
rustc_internal::stable(internal_inst)
56+
}
57+
58+
/// Resolve an unevaluated constant into a (MonoItem, symbol_name) pair.
59+
///
60+
/// This wraps `middle::ty::Instance::try_resolve` and the internal mono item
61+
/// symbol name resolution, keeping those internal APIs out of printer.rs.
62+
pub fn resolve_unevaluated_const(
63+
tcx: TyCtxt<'_>,
64+
def_id: stable_mir::DefId,
65+
args: stable_mir::ty::GenericArgs,
66+
) -> (stable_mir::mir::mono::MonoItem, String) {
67+
use super::middle::ty::TypingEnv;
68+
let internal_def = rustc_internal::internal(tcx, def_id);
69+
let internal_args = rustc_internal::internal(tcx, args);
70+
let maybe_inst = middle::ty::Instance::try_resolve(
71+
tcx,
72+
TypingEnv::post_analysis(tcx, internal_def),
73+
internal_def,
74+
internal_args,
75+
);
76+
let inst = maybe_inst
77+
.ok()
78+
.flatten()
79+
.unwrap_or_else(|| panic!("Failed to resolve mono item for def {:?}", def_id));
80+
let internal_mono_item = middle::mir::mono::MonoItem::Fn(inst);
81+
let item_name = crate::compat::mono_collect::mono_item_name_int(tcx, &internal_mono_item);
82+
(rustc_internal::stable(internal_mono_item), item_name)
83+
}
84+
85+
/// Extract an `OpaqueInstanceKind` from a stable MIR `Instance` by
86+
/// converting to the internal representation and capturing the debug
87+
/// string and reify-shim flag.
88+
pub fn instance_kind(tcx: TyCtxt<'_>, inst: &Instance) -> OpaqueInstanceKind {
89+
let internal_inst = rustc_internal::internal(tcx, inst);
90+
let kind = internal_inst.def;
91+
OpaqueInstanceKind {
92+
debug_repr: format!("{:?}", kind),
93+
is_reify_shim: matches!(kind, middle::ty::InstanceKind::ReifyShim(..)),
94+
}
95+
}

src/compat/mod.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//! Compatibility layer for rustc internal APIs.
2+
//!
3+
//! Every direct `rustc_*` import and raw `TyCtxt` query lives inside this
4+
//! module (or one of its submodules). Code outside `compat` (and `driver.rs`)
5+
//! should never touch rustc internals directly; it should go through the
6+
//! types and functions re-exported here instead.
7+
//!
8+
//! The payoff: when a nightly toolchain upgrade moves or renames an internal
9+
//! API, the fix stays inside `compat/` and nothing else needs to change.
10+
//!
11+
//! # Submodules
12+
//!
13+
//! | Module | Purpose |
14+
//! |--------|---------|
15+
//! | [`bridge`] | Stable-to-internal conversions (`Instance`, `InstanceKind`, unevaluated consts) |
16+
//! | [`mono_collect`] | Monomorphization collection and symbol naming |
17+
//! | [`output`] | Output filename resolution from the compiler session |
18+
//! | [`spans`] | Span-to-source-location resolution |
19+
//! | [`types`] | Type queries: generics, signatures, discriminants, attributes |
20+
//!
21+
//! # Re-exports
22+
//!
23+
//! The crate-level re-exports below give callers access to the handful of
24+
//! rustc types that inevitably appear in public signatures (`TyCtxt`,
25+
//! `DefId`, etc.) without requiring them to know which rustc crate the
26+
//! type actually lives in.
27+
28+
pub extern crate rustc_middle;
29+
pub extern crate rustc_monomorphize;
30+
pub extern crate rustc_session;
31+
pub extern crate rustc_smir;
32+
pub extern crate rustc_span;
33+
pub extern crate stable_mir;
34+
35+
// We use rustc's vendored serde rather than pulling in our own copy.
36+
// Having two serde versions causes version-mismatch errors when
37+
// serializing types that come from the compiler.
38+
pub extern crate serde;
39+
pub extern crate serde_json;
40+
41+
/// Alias for `rustc_middle`; keeps import paths shorter.
42+
pub use rustc_middle as middle;
43+
/// The compiler's typing context; threaded through most compat functions.
44+
pub use rustc_middle::ty::TyCtxt;
45+
/// Bridge between stable MIR types and rustc internals.
46+
pub use rustc_smir::rustc_internal;
47+
/// Convenience re-export: converts a stable MIR value to its internal rustc
48+
/// counterpart.
49+
pub use rustc_smir::rustc_internal::internal;
50+
/// Rustc's definition identifier. Re-exported so callers outside `compat`
51+
/// don't need to depend on `rustc_span` directly.
52+
pub use rustc_span::def_id::DefId;
53+
54+
pub mod bridge;
55+
pub mod mono_collect;
56+
pub mod output;
57+
pub mod spans;
58+
pub mod types;

src/compat/mono_collect.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//! Mono item collection and symbol naming.
2+
//!
3+
//! Wraps `tcx.collect_and_partition_mono_items()`, `item.symbol_name()`,
4+
//! and the `rustc_internal` stable/internal conversions needed for naming.
5+
6+
use super::middle;
7+
use super::rustc_internal;
8+
use super::stable_mir;
9+
use super::TyCtxt;
10+
use stable_mir::mir::mono::MonoItem;
11+
12+
/// Collect all monomorphized items from the compiler.
13+
pub fn mono_collect(tcx: TyCtxt<'_>) -> Vec<MonoItem> {
14+
let units = tcx.collect_and_partition_mono_items(()).1;
15+
units
16+
.iter()
17+
.flat_map(|unit| {
18+
unit.items_in_deterministic_order(tcx)
19+
.iter()
20+
.map(|(internal_item, _)| rustc_internal::stable(internal_item))
21+
.collect::<Vec<_>>()
22+
})
23+
.collect()
24+
}
25+
26+
/// Get the symbol name for a mono item (the mangled linker name).
27+
pub fn mono_item_name(tcx: TyCtxt<'_>, item: &MonoItem) -> String {
28+
if let MonoItem::GlobalAsm(data) = item {
29+
crate::printer::hash(data).to_string()
30+
} else {
31+
mono_item_name_int(tcx, &rustc_internal::internal(tcx, item))
32+
}
33+
}
34+
35+
/// Get the symbol name for an internal (non-stable) mono item.
36+
pub fn mono_item_name_int<'a>(tcx: TyCtxt<'a>, item: &middle::mir::mono::MonoItem<'a>) -> String {
37+
item.symbol_name(tcx).name.into()
38+
}

src/compat/output.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//! Output filename resolution.
2+
//!
3+
//! Wraps `tcx.output_filenames().path(OutputType::Mir)` so that callers
4+
//! don't need to import `rustc_session` directly.
5+
6+
use std::path::PathBuf;
7+
8+
use super::rustc_session::config::{OutFileName, OutputType};
9+
use super::TyCtxt;
10+
11+
/// Resolved output destination for MIR-derived files.
12+
pub enum OutputDest {
13+
Stdout,
14+
File(PathBuf),
15+
}
16+
17+
/// Resolve the MIR output path from the compiler session, replacing
18+
/// the extension with the given one.
19+
pub fn mir_output_path(tcx: TyCtxt<'_>, extension: &str) -> OutputDest {
20+
match tcx.output_filenames(()).path(OutputType::Mir) {
21+
OutFileName::Stdout => OutputDest::Stdout,
22+
OutFileName::Real(path) => OutputDest::File(path.with_extension(extension)),
23+
}
24+
}

src/compat/spans.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! Span-to-source-location resolution.
2+
//!
3+
//! Wraps the `source_map().span_to_location_info()` internal API
4+
//! so that callers don't need to touch `rustc_span` directly.
5+
6+
use super::internal;
7+
use super::rustc_span;
8+
use super::stable_mir;
9+
use super::TyCtxt;
10+
use stable_mir::ty::Span;
11+
12+
/// Source location tuple: `(file, lo_line, lo_col, hi_line, hi_col)`.
13+
pub type SourceData = (String, usize, usize, usize, usize);
14+
15+
/// Resolve a stable MIR span to a (file, lo_line, lo_col, hi_line, hi_col) tuple.
16+
pub fn resolve_span(tcx: TyCtxt<'_>, span: &Span) -> SourceData {
17+
let span_internal = internal(tcx, span);
18+
let (source_file, lo_line, lo_col, hi_line, hi_col) =
19+
tcx.sess.source_map().span_to_location_info(span_internal);
20+
let file_name = match source_file {
21+
Some(sf) => sf
22+
.name
23+
.display(rustc_span::FileNameDisplayPreference::Remapped)
24+
.to_string(),
25+
None => "no-location".to_string(),
26+
};
27+
(file_name, lo_line, lo_col, hi_line, hi_col)
28+
}

0 commit comments

Comments
 (0)