Skip to content

Commit 01c06e9

Browse files
authored
Rollup merge of #108846 - celinval:smir-poc, r=oli-obk
StableMIR: Proof-of-concept implementation + test This PR is part of the [project Stable MIR](https://github.com/rust-lang/project-stable-mir). The PR deletes old re-exports from rustc_smir and introduces a proof-of-concept implementation for APIs to retrieve crate information. The implementation follows the [design described here](https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view), but instead of using separate crates for the implementation, it uses separate modules inside `rustc_smir`. The API introduced at this point should be seen just as an example on how we are planning to structure the communication between tools and the compiler. I have not explored yet what should be the right granularity, the best starting point for users, neither the best way to implement it. r? `@oli-obk`
2 parents 8b9fa5d + 5eaeb71 commit 01c06e9

File tree

11 files changed

+274
-66
lines changed

11 files changed

+274
-66
lines changed

Cargo.lock

+2-8
Original file line numberDiff line numberDiff line change
@@ -4669,15 +4669,9 @@ dependencies = [
46694669
name = "rustc_smir"
46704670
version = "0.0.0"
46714671
dependencies = [
4672-
"rustc_borrowck",
4673-
"rustc_driver",
4674-
"rustc_hir",
4675-
"rustc_interface",
46764672
"rustc_middle",
4677-
"rustc_mir_dataflow",
4678-
"rustc_mir_transform",
4679-
"rustc_serialize",
4680-
"rustc_trait_selection",
4673+
"rustc_span",
4674+
"tracing",
46814675
]
46824676

46834677
[[package]]

compiler/rustc_smir/Cargo.toml

+3-16
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,12 @@ version = "0.0.0"
44
edition = "2021"
55

66
[dependencies]
7-
rustc_borrowck = { path = "../rustc_borrowck", optional = true }
8-
rustc_driver = { path = "../rustc_driver", optional = true }
9-
rustc_hir = { path = "../rustc_hir", optional = true }
10-
rustc_interface = { path = "../rustc_interface", optional = true }
117
rustc_middle = { path = "../rustc_middle", optional = true }
12-
rustc_mir_dataflow = { path = "../rustc_mir_dataflow", optional = true }
13-
rustc_mir_transform = { path = "../rustc_mir_transform", optional = true }
14-
rustc_serialize = { path = "../rustc_serialize", optional = true }
15-
rustc_trait_selection = { path = "../rustc_trait_selection", optional = true }
8+
rustc_span = { path = "../rustc_span", optional = true }
9+
tracing = "0.1"
1610

1711
[features]
1812
default = [
19-
"rustc_borrowck",
20-
"rustc_driver",
21-
"rustc_hir",
22-
"rustc_interface",
2313
"rustc_middle",
24-
"rustc_mir_dataflow",
25-
"rustc_mir_transform",
26-
"rustc_serialize",
27-
"rustc_trait_selection",
14+
"rustc_span",
2815
]

compiler/rustc_smir/README.md

+37
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,40 @@ git subtree pull --prefix=compiler/rustc_smir https://github.com/rust-lang/proje
7373
Note: only ever sync to rustc from the project-stable-mir's `smir` branch. Do not sync with your own forks.
7474

7575
Then open a PR against rustc just like a regular PR.
76+
77+
## Stable MIR Design
78+
79+
The stable-mir will follow a similar approach to proc-macro2. It’s
80+
implementation will eventually be broken down into two main crates:
81+
82+
- `stable_mir`: Public crate, to be published on crates.io, which will contain
83+
the stable data structure as well as proxy APIs to make calls to the
84+
compiler.
85+
- `rustc_smir`: The compiler crate that will translate from internal MIR to
86+
SMIR. This crate will also implement APIs that will be invoked by
87+
stable-mir to query the compiler for more information.
88+
89+
This will help tools to communicate with the rust compiler via stable APIs. Tools will depend on
90+
`stable_mir` crate, which will invoke the compiler using APIs defined in `rustc_smir`. I.e.:
91+
92+
```
93+
┌──────────────────────────────────┐ ┌──────────────────────────────────┐
94+
│ External Tool ┌──────────┐ │ │ ┌──────────┐ Rust Compiler │
95+
│ │ │ │ │ │ │ │
96+
│ │stable_mir| │ │ │rustc_smir│ │
97+
│ │ │ ├──────────►| │ │ │
98+
│ │ │ │◄──────────┤ │ │ │
99+
│ │ │ │ │ │ │ │
100+
│ │ │ │ │ │ │ │
101+
│ └──────────┘ │ │ └──────────┘ │
102+
└──────────────────────────────────┘ └──────────────────────────────────┘
103+
```
104+
105+
More details can be found here:
106+
https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view
107+
108+
For now, the code for these two crates are in separate modules of this crate.
109+
The modules have the same name for simplicity. We also have a third module,
110+
`rustc_internal` which will expose APIs and definitions that allow users to
111+
gather information from internal MIR constructs that haven't been exposed in
112+
the `stable_mir` module.
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[toolchain]
2-
channel = "nightly-2022-06-01"
2+
channel = "nightly-2023-02-28"
33
components = [ "rustfmt", "rustc-dev" ]

compiler/rustc_smir/src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
test(attr(allow(unused_variables), deny(warnings)))
1212
)]
1313
#![cfg_attr(not(feature = "default"), feature(rustc_private))]
14-
#![deny(rustc::untranslatable_diagnostic)]
15-
#![deny(rustc::diagnostic_outside_of_impl)]
1614

17-
pub mod mir;
15+
pub mod rustc_internal;
16+
pub mod stable_mir;
1817

19-
pub mod very_unstable;
18+
// Make this module private for now since external users should not call these directly.
19+
mod rustc_smir;

compiler/rustc_smir/src/mir.rs

-10
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//! Module that implements the bridge between Stable MIR and internal compiler MIR.
2+
//!
3+
//! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs
4+
//! until stable MIR is complete.
5+
6+
use crate::stable_mir;
7+
pub use rustc_span::def_id::{CrateNum, DefId};
8+
9+
pub fn item_def_id(item: &stable_mir::CrateItem) -> DefId {
10+
item.0
11+
}
12+
13+
pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
14+
item.id.into()
15+
}
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//! Module that implements what will become the rustc side of Stable MIR.
2+
//!
3+
//! This module is responsible for building Stable MIR components from internal components.
4+
//!
5+
//! This module is not intended to be invoked directly by users. It will eventually
6+
//! become the public API of rustc that will be invoked by the `stable_mir` crate.
7+
//!
8+
//! For now, we are developing everything inside `rustc`, thus, we keep this module private.
9+
10+
use crate::stable_mir::{self};
11+
use rustc_middle::ty::{tls::with, TyCtxt};
12+
use rustc_span::def_id::{CrateNum, LOCAL_CRATE};
13+
use tracing::debug;
14+
15+
/// Get information about the local crate.
16+
pub fn local_crate() -> stable_mir::Crate {
17+
with(|tcx| smir_crate(tcx, LOCAL_CRATE))
18+
}
19+
20+
/// Retrieve a list of all external crates.
21+
pub fn external_crates() -> Vec<stable_mir::Crate> {
22+
with(|tcx| tcx.crates(()).iter().map(|crate_num| smir_crate(tcx, *crate_num)).collect())
23+
}
24+
25+
/// Find a crate with the given name.
26+
pub fn find_crate(name: &str) -> Option<stable_mir::Crate> {
27+
with(|tcx| {
28+
[LOCAL_CRATE].iter().chain(tcx.crates(()).iter()).find_map(|crate_num| {
29+
let crate_name = tcx.crate_name(*crate_num).to_string();
30+
(name == crate_name).then(|| smir_crate(tcx, *crate_num))
31+
})
32+
})
33+
}
34+
35+
/// Retrieve all items of the local crate that have a MIR associated with them.
36+
pub fn all_local_items() -> stable_mir::CrateItems {
37+
with(|tcx| {
38+
tcx.mir_keys(()).iter().map(|item| stable_mir::CrateItem(item.to_def_id())).collect()
39+
})
40+
}
41+
42+
/// Build a stable mir crate from a given crate number.
43+
fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate {
44+
let crate_name = tcx.crate_name(crate_num).to_string();
45+
let is_local = crate_num == LOCAL_CRATE;
46+
debug!(?crate_name, ?crate_num, "smir_crate");
47+
stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local }
48+
}
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//! Module that implements the public interface to the Stable MIR.
2+
//!
3+
//! This module shall contain all type definitions and APIs that we expect 3P tools to invoke to
4+
//! interact with the compiler.
5+
//!
6+
//! The goal is to eventually move this module to its own crate which shall be published on
7+
//! [crates.io](https://crates.io).
8+
//!
9+
//! ## Note:
10+
//!
11+
//! There shouldn't be any direct references to internal compiler constructs in this module.
12+
//! If you need an internal construct, consider using `rustc_internal` or `rustc_smir`.
13+
14+
use crate::rustc_internal;
15+
16+
/// Use String for now but we should replace it.
17+
pub type Symbol = String;
18+
19+
/// The number that identifies a crate.
20+
pub type CrateNum = usize;
21+
22+
/// A unique identification number for each item accessible for the current compilation unit.
23+
pub type DefId = usize;
24+
25+
/// A list of crate items.
26+
pub type CrateItems = Vec<CrateItem>;
27+
28+
/// Holds information about a crate.
29+
#[derive(Clone, PartialEq, Eq, Debug)]
30+
pub struct Crate {
31+
pub(crate) id: CrateNum,
32+
pub name: Symbol,
33+
pub is_local: bool,
34+
}
35+
36+
/// Holds information about an item in the crate.
37+
/// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to
38+
/// use this item.
39+
#[derive(Clone, PartialEq, Eq, Debug)]
40+
pub struct CrateItem(pub(crate) rustc_internal::DefId);
41+
42+
/// Access to the local crate.
43+
pub fn local_crate() -> Crate {
44+
crate::rustc_smir::local_crate()
45+
}
46+
47+
/// Try to find a crate with the given name.
48+
pub fn find_crate(name: &str) -> Option<Crate> {
49+
crate::rustc_smir::find_crate(name)
50+
}
51+
52+
/// Try to find a crate with the given name.
53+
pub fn external_crates() -> Vec<Crate> {
54+
crate::rustc_smir::external_crates()
55+
}
56+
57+
/// Retrieve all items in the local crate that have a MIR associated with them.
58+
pub fn all_local_items() -> CrateItems {
59+
crate::rustc_smir::all_local_items()
60+
}

compiler/rustc_smir/src/very_unstable.rs

-27
This file was deleted.
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// run-pass
2+
// Test that users are able to use stable mir APIs to retrieve information of the current crate
3+
4+
// ignore-stage-1
5+
// ignore-cross-compile
6+
// ignore-remote
7+
8+
#![feature(rustc_private)]
9+
10+
extern crate rustc_driver;
11+
extern crate rustc_hir;
12+
extern crate rustc_interface;
13+
extern crate rustc_middle;
14+
extern crate rustc_smir;
15+
16+
use rustc_driver::{Callbacks, Compilation, RunCompiler};
17+
use rustc_hir::def::DefKind;
18+
use rustc_interface::{interface, Queries};
19+
use rustc_middle::ty::TyCtxt;
20+
use rustc_smir::{rustc_internal, stable_mir};
21+
use std::io::Write;
22+
23+
const CRATE_NAME: &str = "input";
24+
25+
/// This function uses the Stable MIR APIs to get information about the test crate.
26+
fn test_stable_mir(tcx: TyCtxt<'_>) {
27+
// Get the local crate using stable_mir API.
28+
let local = stable_mir::local_crate();
29+
assert_eq!(&local.name, CRATE_NAME);
30+
31+
// Find items in the local crate.
32+
let items = stable_mir::all_local_items();
33+
assert!(has_item(tcx, &items, (DefKind::Fn, "foo_bar")));
34+
assert!(has_item(tcx, &items, (DefKind::Fn, "foo::bar")));
35+
36+
// Find the `std` crate.
37+
assert!(stable_mir::find_crate("std").is_some());
38+
}
39+
40+
// Use internal API to find a function in a crate.
41+
fn has_item(tcx: TyCtxt, items: &stable_mir::CrateItems, item: (DefKind, &str)) -> bool {
42+
items.iter().any(|crate_item| {
43+
let def_id = rustc_internal::item_def_id(crate_item);
44+
tcx.def_kind(def_id) == item.0 && tcx.def_path_str(def_id) == item.1
45+
})
46+
}
47+
48+
/// This test will generate and analyze a dummy crate using the stable mir.
49+
/// For that, it will first write the dummy crate into a file.
50+
/// It will invoke the compiler using a custom Callback implementation, which will
51+
/// invoke Stable MIR APIs after the compiler has finished its analysis.
52+
fn main() {
53+
let path = "input.rs";
54+
generate_input(&path).unwrap();
55+
let args = vec![
56+
"rustc".to_string(),
57+
"--crate-type=lib".to_string(),
58+
"--crate-name".to_string(),
59+
CRATE_NAME.to_string(),
60+
path.to_string(),
61+
];
62+
rustc_driver::catch_fatal_errors(|| {
63+
RunCompiler::new(&args, &mut SMirCalls {}).run().unwrap();
64+
})
65+
.unwrap();
66+
}
67+
68+
struct SMirCalls {}
69+
70+
impl Callbacks for SMirCalls {
71+
/// Called after analysis. Return value instructs the compiler whether to
72+
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
73+
fn after_analysis<'tcx>(
74+
&mut self,
75+
_compiler: &interface::Compiler,
76+
queries: &'tcx Queries<'tcx>,
77+
) -> Compilation {
78+
queries.global_ctxt().unwrap().enter(|tcx| {
79+
test_stable_mir(tcx);
80+
});
81+
// No need to keep going.
82+
Compilation::Stop
83+
}
84+
}
85+
86+
fn generate_input(path: &str) -> std::io::Result<()> {
87+
let mut file = std::fs::File::create(path)?;
88+
write!(
89+
file,
90+
r#"
91+
mod foo {{
92+
pub fn bar(i: i32) -> i64 {{
93+
i as i64
94+
}}
95+
}}
96+
97+
pub fn foo_bar(x: i32, y: i32) -> i64 {{
98+
let x_64 = foo::bar(x);
99+
let y_64 = foo::bar(y);
100+
x_64.wrapping_add(y_64)
101+
}}"#
102+
)?;
103+
Ok(())
104+
}

0 commit comments

Comments
 (0)