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``````
This commit is contained in:
commit
9b6b7a3e84
10
Cargo.lock
10
Cargo.lock
@ -4669,15 +4669,9 @@ dependencies = [
|
||||
name = "rustc_smir"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_borrowck",
|
||||
"rustc_driver",
|
||||
"rustc_hir",
|
||||
"rustc_interface",
|
||||
"rustc_middle",
|
||||
"rustc_mir_dataflow",
|
||||
"rustc_mir_transform",
|
||||
"rustc_serialize",
|
||||
"rustc_trait_selection",
|
||||
"rustc_span",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4,25 +4,12 @@ version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rustc_borrowck = { path = "../rustc_borrowck", optional = true }
|
||||
rustc_driver = { path = "../rustc_driver", optional = true }
|
||||
rustc_hir = { path = "../rustc_hir", optional = true }
|
||||
rustc_interface = { path = "../rustc_interface", optional = true }
|
||||
rustc_middle = { path = "../rustc_middle", optional = true }
|
||||
rustc_mir_dataflow = { path = "../rustc_mir_dataflow", optional = true }
|
||||
rustc_mir_transform = { path = "../rustc_mir_transform", optional = true }
|
||||
rustc_serialize = { path = "../rustc_serialize", optional = true }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection", optional = true }
|
||||
rustc_span = { path = "../rustc_span", optional = true }
|
||||
tracing = "0.1"
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"rustc_borrowck",
|
||||
"rustc_driver",
|
||||
"rustc_hir",
|
||||
"rustc_interface",
|
||||
"rustc_middle",
|
||||
"rustc_mir_dataflow",
|
||||
"rustc_mir_transform",
|
||||
"rustc_serialize",
|
||||
"rustc_trait_selection",
|
||||
"rustc_span",
|
||||
]
|
||||
|
@ -73,3 +73,40 @@ git subtree pull --prefix=compiler/rustc_smir https://github.com/rust-lang/proje
|
||||
Note: only ever sync to rustc from the project-stable-mir's `smir` branch. Do not sync with your own forks.
|
||||
|
||||
Then open a PR against rustc just like a regular PR.
|
||||
|
||||
## Stable MIR Design
|
||||
|
||||
The stable-mir will follow a similar approach to proc-macro2. It’s
|
||||
implementation will eventually be broken down into two main crates:
|
||||
|
||||
- `stable_mir`: Public crate, to be published on crates.io, which will contain
|
||||
the stable data structure as well as proxy APIs to make calls to the
|
||||
compiler.
|
||||
- `rustc_smir`: The compiler crate that will translate from internal MIR to
|
||||
SMIR. This crate will also implement APIs that will be invoked by
|
||||
stable-mir to query the compiler for more information.
|
||||
|
||||
This will help tools to communicate with the rust compiler via stable APIs. Tools will depend on
|
||||
`stable_mir` crate, which will invoke the compiler using APIs defined in `rustc_smir`. I.e.:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────┐ ┌──────────────────────────────────┐
|
||||
│ External Tool ┌──────────┐ │ │ ┌──────────┐ Rust Compiler │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │stable_mir| │ │ │rustc_smir│ │
|
||||
│ │ │ ├──────────►| │ │ │
|
||||
│ │ │ │◄──────────┤ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ └──────────┘ │ │ └──────────┘ │
|
||||
└──────────────────────────────────┘ └──────────────────────────────────┘
|
||||
```
|
||||
|
||||
More details can be found here:
|
||||
https://hackmd.io/XhnYHKKuR6-LChhobvlT-g?view
|
||||
|
||||
For now, the code for these two crates are in separate modules of this crate.
|
||||
The modules have the same name for simplicity. We also have a third module,
|
||||
`rustc_internal` which will expose APIs and definitions that allow users to
|
||||
gather information from internal MIR constructs that haven't been exposed in
|
||||
the `stable_mir` module.
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2022-06-01"
|
||||
channel = "nightly-2023-02-28"
|
||||
components = [ "rustfmt", "rustc-dev" ]
|
||||
|
@ -11,9 +11,9 @@
|
||||
test(attr(allow(unused_variables), deny(warnings)))
|
||||
)]
|
||||
#![cfg_attr(not(feature = "default"), feature(rustc_private))]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
||||
pub mod mir;
|
||||
pub mod rustc_internal;
|
||||
pub mod stable_mir;
|
||||
|
||||
pub mod very_unstable;
|
||||
// Make this module private for now since external users should not call these directly.
|
||||
mod rustc_smir;
|
||||
|
@ -1,10 +0,0 @@
|
||||
pub use crate::very_unstable::hir::ImplicitSelfKind;
|
||||
pub use crate::very_unstable::middle::mir::{
|
||||
visit::MutVisitor, AggregateKind, AssertKind, BasicBlock, BasicBlockData, BinOp, BindingForm,
|
||||
BlockTailInfo, Body, BorrowKind, CastKind, ClearCrossCrate, Constant, ConstantKind,
|
||||
CopyNonOverlapping, Coverage, FakeReadCause, Field, GeneratorInfo, InlineAsmOperand, Local,
|
||||
LocalDecl, LocalInfo, LocalKind, Location, MirPhase, MirSource, NullOp, Operand, Place,
|
||||
PlaceRef, ProjectionElem, ProjectionKind, Promoted, RetagKind, Rvalue, Safety, SourceInfo,
|
||||
SourceScope, SourceScopeData, SourceScopeLocalData, Statement, StatementKind, UnOp,
|
||||
UserTypeProjection, UserTypeProjections, VarBindingForm, VarDebugInfo, VarDebugInfoContents,
|
||||
};
|
15
compiler/rustc_smir/src/rustc_internal/mod.rs
Normal file
15
compiler/rustc_smir/src/rustc_internal/mod.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//! Module that implements the bridge between Stable MIR and internal compiler MIR.
|
||||
//!
|
||||
//! For that, we define APIs that will temporarily be public to 3P that exposes rustc internal APIs
|
||||
//! until stable MIR is complete.
|
||||
|
||||
use crate::stable_mir;
|
||||
pub use rustc_span::def_id::{CrateNum, DefId};
|
||||
|
||||
pub fn item_def_id(item: &stable_mir::CrateItem) -> DefId {
|
||||
item.0
|
||||
}
|
||||
|
||||
pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
|
||||
item.id.into()
|
||||
}
|
48
compiler/rustc_smir/src/rustc_smir/mod.rs
Normal file
48
compiler/rustc_smir/src/rustc_smir/mod.rs
Normal file
@ -0,0 +1,48 @@
|
||||
//! Module that implements what will become the rustc side of Stable MIR.
|
||||
//!
|
||||
//! This module is responsible for building Stable MIR components from internal components.
|
||||
//!
|
||||
//! This module is not intended to be invoked directly by users. It will eventually
|
||||
//! become the public API of rustc that will be invoked by the `stable_mir` crate.
|
||||
//!
|
||||
//! For now, we are developing everything inside `rustc`, thus, we keep this module private.
|
||||
|
||||
use crate::stable_mir::{self};
|
||||
use rustc_middle::ty::{tls::with, TyCtxt};
|
||||
use rustc_span::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use tracing::debug;
|
||||
|
||||
/// Get information about the local crate.
|
||||
pub fn local_crate() -> stable_mir::Crate {
|
||||
with(|tcx| smir_crate(tcx, LOCAL_CRATE))
|
||||
}
|
||||
|
||||
/// Retrieve a list of all external crates.
|
||||
pub fn external_crates() -> Vec<stable_mir::Crate> {
|
||||
with(|tcx| tcx.crates(()).iter().map(|crate_num| smir_crate(tcx, *crate_num)).collect())
|
||||
}
|
||||
|
||||
/// Find a crate with the given name.
|
||||
pub fn find_crate(name: &str) -> Option<stable_mir::Crate> {
|
||||
with(|tcx| {
|
||||
[LOCAL_CRATE].iter().chain(tcx.crates(()).iter()).find_map(|crate_num| {
|
||||
let crate_name = tcx.crate_name(*crate_num).to_string();
|
||||
(name == crate_name).then(|| smir_crate(tcx, *crate_num))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve all items of the local crate that have a MIR associated with them.
|
||||
pub fn all_local_items() -> stable_mir::CrateItems {
|
||||
with(|tcx| {
|
||||
tcx.mir_keys(()).iter().map(|item| stable_mir::CrateItem(item.to_def_id())).collect()
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a stable mir crate from a given crate number.
|
||||
fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate {
|
||||
let crate_name = tcx.crate_name(crate_num).to_string();
|
||||
let is_local = crate_num == LOCAL_CRATE;
|
||||
debug!(?crate_name, ?crate_num, "smir_crate");
|
||||
stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local }
|
||||
}
|
60
compiler/rustc_smir/src/stable_mir/mod.rs
Normal file
60
compiler/rustc_smir/src/stable_mir/mod.rs
Normal file
@ -0,0 +1,60 @@
|
||||
//! Module that implements the public interface to the Stable MIR.
|
||||
//!
|
||||
//! This module shall contain all type definitions and APIs that we expect 3P tools to invoke to
|
||||
//! interact with the compiler.
|
||||
//!
|
||||
//! The goal is to eventually move this module to its own crate which shall be published on
|
||||
//! [crates.io](https://crates.io).
|
||||
//!
|
||||
//! ## Note:
|
||||
//!
|
||||
//! There shouldn't be any direct references to internal compiler constructs in this module.
|
||||
//! If you need an internal construct, consider using `rustc_internal` or `rustc_smir`.
|
||||
|
||||
use crate::rustc_internal;
|
||||
|
||||
/// Use String for now but we should replace it.
|
||||
pub type Symbol = String;
|
||||
|
||||
/// The number that identifies a crate.
|
||||
pub type CrateNum = usize;
|
||||
|
||||
/// A unique identification number for each item accessible for the current compilation unit.
|
||||
pub type DefId = usize;
|
||||
|
||||
/// A list of crate items.
|
||||
pub type CrateItems = Vec<CrateItem>;
|
||||
|
||||
/// Holds information about a crate.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Crate {
|
||||
pub(crate) id: CrateNum,
|
||||
pub name: Symbol,
|
||||
pub is_local: bool,
|
||||
}
|
||||
|
||||
/// Holds information about an item in the crate.
|
||||
/// For now, it only stores the item DefId. Use functions inside `rustc_internal` module to
|
||||
/// use this item.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct CrateItem(pub(crate) rustc_internal::DefId);
|
||||
|
||||
/// Access to the local crate.
|
||||
pub fn local_crate() -> Crate {
|
||||
crate::rustc_smir::local_crate()
|
||||
}
|
||||
|
||||
/// Try to find a crate with the given name.
|
||||
pub fn find_crate(name: &str) -> Option<Crate> {
|
||||
crate::rustc_smir::find_crate(name)
|
||||
}
|
||||
|
||||
/// Try to find a crate with the given name.
|
||||
pub fn external_crates() -> Vec<Crate> {
|
||||
crate::rustc_smir::external_crates()
|
||||
}
|
||||
|
||||
/// Retrieve all items in the local crate that have a MIR associated with them.
|
||||
pub fn all_local_items() -> CrateItems {
|
||||
crate::rustc_smir::all_local_items()
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
//! This module reexports various crates and modules from unstable rustc APIs.
|
||||
//! Add anything you need here and it will get slowly transferred to a stable API.
|
||||
//! Only use rustc_smir in your dependencies and use the reexports here instead of
|
||||
//! directly referring to the unstable crates.
|
||||
|
||||
macro_rules! crates {
|
||||
($($rustc_name:ident -> $name:ident,)*) => {
|
||||
$(
|
||||
#[cfg(not(feature = "default"))]
|
||||
pub extern crate $rustc_name as $name;
|
||||
#[cfg(feature = "default")]
|
||||
pub use $rustc_name as $name;
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
crates! {
|
||||
rustc_borrowck -> borrowck,
|
||||
rustc_driver -> driver,
|
||||
rustc_hir -> hir,
|
||||
rustc_interface -> interface,
|
||||
rustc_middle -> middle,
|
||||
rustc_mir_dataflow -> dataflow,
|
||||
rustc_mir_transform -> transform,
|
||||
rustc_serialize -> serialize,
|
||||
rustc_trait_selection -> trait_selection,
|
||||
}
|
104
tests/ui-fulldeps/stable-mir/crate-info.rs
Normal file
104
tests/ui-fulldeps/stable-mir/crate-info.rs
Normal file
@ -0,0 +1,104 @@
|
||||
// run-pass
|
||||
// Test that users are able to use stable mir APIs to retrieve information of the current crate
|
||||
|
||||
// ignore-stage-1
|
||||
// ignore-cross-compile
|
||||
// ignore-remote
|
||||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_interface;
|
||||
extern crate rustc_middle;
|
||||
extern crate rustc_smir;
|
||||
|
||||
use rustc_driver::{Callbacks, Compilation, RunCompiler};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_interface::{interface, Queries};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_smir::{rustc_internal, stable_mir};
|
||||
use std::io::Write;
|
||||
|
||||
const CRATE_NAME: &str = "input";
|
||||
|
||||
/// This function uses the Stable MIR APIs to get information about the test crate.
|
||||
fn test_stable_mir(tcx: TyCtxt<'_>) {
|
||||
// Get the local crate using stable_mir API.
|
||||
let local = stable_mir::local_crate();
|
||||
assert_eq!(&local.name, CRATE_NAME);
|
||||
|
||||
// Find items in the local crate.
|
||||
let items = stable_mir::all_local_items();
|
||||
assert!(has_item(tcx, &items, (DefKind::Fn, "foo_bar")));
|
||||
assert!(has_item(tcx, &items, (DefKind::Fn, "foo::bar")));
|
||||
|
||||
// Find the `std` crate.
|
||||
assert!(stable_mir::find_crate("std").is_some());
|
||||
}
|
||||
|
||||
// Use internal API to find a function in a crate.
|
||||
fn has_item(tcx: TyCtxt, items: &stable_mir::CrateItems, item: (DefKind, &str)) -> bool {
|
||||
items.iter().any(|crate_item| {
|
||||
let def_id = rustc_internal::item_def_id(crate_item);
|
||||
tcx.def_kind(def_id) == item.0 && tcx.def_path_str(def_id) == item.1
|
||||
})
|
||||
}
|
||||
|
||||
/// This test will generate and analyze a dummy crate using the stable mir.
|
||||
/// For that, it will first write the dummy crate into a file.
|
||||
/// It will invoke the compiler using a custom Callback implementation, which will
|
||||
/// invoke Stable MIR APIs after the compiler has finished its analysis.
|
||||
fn main() {
|
||||
let path = "input.rs";
|
||||
generate_input(&path).unwrap();
|
||||
let args = vec![
|
||||
"rustc".to_string(),
|
||||
"--crate-type=lib".to_string(),
|
||||
"--crate-name".to_string(),
|
||||
CRATE_NAME.to_string(),
|
||||
path.to_string(),
|
||||
];
|
||||
rustc_driver::catch_fatal_errors(|| {
|
||||
RunCompiler::new(&args, &mut SMirCalls {}).run().unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
struct SMirCalls {}
|
||||
|
||||
impl Callbacks for SMirCalls {
|
||||
/// Called after analysis. Return value instructs the compiler whether to
|
||||
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
|
||||
fn after_analysis<'tcx>(
|
||||
&mut self,
|
||||
_compiler: &interface::Compiler,
|
||||
queries: &'tcx Queries<'tcx>,
|
||||
) -> Compilation {
|
||||
queries.global_ctxt().unwrap().enter(|tcx| {
|
||||
test_stable_mir(tcx);
|
||||
});
|
||||
// No need to keep going.
|
||||
Compilation::Stop
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_input(path: &str) -> std::io::Result<()> {
|
||||
let mut file = std::fs::File::create(path)?;
|
||||
write!(
|
||||
file,
|
||||
r#"
|
||||
mod foo {{
|
||||
pub fn bar(i: i32) -> i64 {{
|
||||
i as i64
|
||||
}}
|
||||
}}
|
||||
|
||||
pub fn foo_bar(x: i32, y: i32) -> i64 {{
|
||||
let x_64 = foo::bar(x);
|
||||
let y_64 = foo::bar(y);
|
||||
x_64.wrapping_add(y_64)
|
||||
}}"#
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user