Skip to content

Commit

Permalink
Create new rustc_smir struct to map future crates
Browse files Browse the repository at this point in the history
+ Add some information to the README.md
  • Loading branch information
celinval committed Mar 7, 2023
1 parent 40185db commit b66db7e
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 27 deletions.
10 changes: 2 additions & 8 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4654,15 +4654,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]]
Expand Down
19 changes: 3 additions & 16 deletions compiler/rustc_smir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
37 changes: 37 additions & 0 deletions compiler/rustc_smir/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion compiler/rustc_smir/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2022-06-01"
channel = "nightly-2023-02-28"
components = [ "rustfmt", "rustc-dev" ]
8 changes: 6 additions & 2 deletions compiler/rustc_smir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +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 rustc_internal;
pub mod stable_mir;

// Make this module private for now since external users should not call these directly.
mod rustc_smir;
12 changes: 12 additions & 0 deletions compiler/rustc_smir/src/rustc_internal/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! 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::CrateItem;

pub type DefId = rustc_span::def_id::DefId;

pub fn item_def_id(item: &CrateItem) -> DefId {
item.0
}
52 changes: 52 additions & 0 deletions compiler/rustc_smir/src/rustc_smir/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! 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, DefId, CRATE_DEF_INDEX, 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))
}

/// 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))
})
})
}

/// 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;
let mod_id = DefId { index: CRATE_DEF_INDEX, krate: crate_num };
let items = if is_local {
tcx.hir_module_items(mod_id.expect_local())
.items()
.map(|item| {
let def_id = item.owner_id.def_id.to_def_id();
stable_mir::CrateItem(def_id)
})
.collect()
} else {
tcx.module_children(mod_id)
.iter()
.filter_map(|item| item.res.opt_def_id())
.map(stable_mir::CrateItem)
.collect::<Vec<_>>()
};
debug!(?crate_name, ?crate_num, "smir_crate");
stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local, root_items: items }
}
52 changes: 52 additions & 0 deletions compiler/rustc_smir/src/stable_mir/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! 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,
/// The items defined in the root of this crate.
pub root_items: CrateItems,
}

/// 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)
}
106 changes: 106 additions & 0 deletions tests/ui-fulldeps/stable-mir/crate-info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// 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.
assert!(has_root_item(tcx, &local, (DefKind::Fn, "foo_bar")));
assert!(has_root_item(tcx, &local, (DefKind::Mod, "foo")));
assert!(!has_root_item(tcx, &local, (DefKind::Fn, "foo::bar")));

// Check that we can find items in the `std` crate.
let std_crate = stable_mir::find_crate("std").unwrap();
assert!(has_root_item(tcx, &std_crate, (DefKind::Mod, "std::any")));
assert!(!has_root_item(tcx, &std_crate, (DefKind::Fn, "std::any::type_name")));
}

// Use internal API to find a function in a crate.
fn has_root_item(tcx: TyCtxt, krate: &stable_mir::Crate, item: (DefKind, &str)) -> bool {
krate.root_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(())
}

0 comments on commit b66db7e

Please sign in to comment.