Skip to content

Commit 0ce579f

Browse files
committed
[StableMIR] API to retrieve definitions from crates
Add functions to retrieve function definitions and static items from all crates (local and external). For external crates, add a query to retrieve the number of defs in a foreign crate.
1 parent f61306d commit 0ce579f

File tree

7 files changed

+225
-4
lines changed

7 files changed

+225
-4
lines changed

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ provide! { tcx, def_id, other, cdata,
387387
crate_hash => { cdata.root.header.hash }
388388
crate_host_hash => { cdata.host_hash }
389389
crate_name => { cdata.root.header.name }
390+
num_extern_def_ids => { cdata.num_def_ids() }
390391

391392
extra_filename => { cdata.root.extra_filename.clone() }
392393

compiler/rustc_middle/src/query/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,16 @@ rustc_queries! {
18121812
desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id) }
18131813
}
18141814

1815+
/// Gets the number of definitions in a foreign crate.
1816+
///
1817+
/// This allows external tools to iterate over all definitions in a foreign crate.
1818+
///
1819+
/// This should never be used for the local crate, instead use `iter_local_def_id`.
1820+
query num_extern_def_ids(_: CrateNum) -> usize {
1821+
desc { "fetching the number of definitions in a crate" }
1822+
separate_provide_extern
1823+
}
1824+
18151825
query lib_features(_: CrateNum) -> &'tcx LibFeatures {
18161826
desc { "calculating the lib features defined in a crate" }
18171827
separate_provide_extern

compiler/rustc_smir/src/rustc_smir/context.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, I
3434

3535
use crate::rustc_internal::RustcInternal;
3636
use crate::rustc_smir::builder::BodyBuilder;
37-
use crate::rustc_smir::{Stable, Tables, alloc, new_item_kind, smir_crate};
37+
use crate::rustc_smir::{Stable, Tables, alloc, filter_def_ids, new_item_kind, smir_crate};
3838

3939
impl<'tcx> Context for TablesWrapper<'tcx> {
4040
fn target_info(&self) -> MachineInfo {
@@ -80,6 +80,20 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
8080
.collect()
8181
}
8282

83+
fn crate_functions(&self, crate_num: CrateNum) -> Vec<FnDef> {
84+
let mut tables = self.0.borrow_mut();
85+
let tcx = tables.tcx;
86+
let krate = crate_num.internal(&mut *tables, tcx);
87+
filter_def_ids(tcx, krate, |def_id| tables.to_fn_def(def_id))
88+
}
89+
90+
fn crate_statics(&self, crate_num: CrateNum) -> Vec<StaticDef> {
91+
let mut tables = self.0.borrow_mut();
92+
let tcx = tables.tcx;
93+
let krate = crate_num.internal(&mut *tables, tcx);
94+
filter_def_ids(tcx, krate, |def_id| tables.to_static(def_id))
95+
}
96+
8397
fn foreign_module(
8498
&self,
8599
mod_def: stable_mir::ty::ForeignModuleDef,

compiler/rustc_smir/src/rustc_smir/mod.rs

+32-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use rustc_middle::mir::interpret::AllocId;
1515
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
1616
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
1717
use stable_mir::abi::Layout;
18-
use stable_mir::mir::mono::InstanceDef;
19-
use stable_mir::ty::{MirConstId, Span, TyConstId};
18+
use stable_mir::mir::mono::{InstanceDef, StaticDef};
19+
use stable_mir::ty::{FnDef, MirConstId, Span, TyConstId};
2020
use stable_mir::{CtorKind, ItemKind};
2121
use tracing::debug;
2222

@@ -79,6 +79,36 @@ impl<'tcx> Tables<'tcx> {
7979
};
8080
!must_override && self.tcx.is_mir_available(def_id)
8181
}
82+
83+
fn to_fn_def(&mut self, def_id: DefId) -> Option<FnDef> {
84+
if matches!(self.tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
85+
Some(self.fn_def(def_id))
86+
} else {
87+
None
88+
}
89+
}
90+
91+
fn to_static(&mut self, def_id: DefId) -> Option<StaticDef> {
92+
matches!(self.tcx.def_kind(def_id), DefKind::Static { .. }).then(|| self.static_def(def_id))
93+
}
94+
}
95+
96+
/// Iterate over the definitions of the given crate.
97+
pub(crate) fn filter_def_ids<F, T>(tcx: TyCtxt<'_>, krate: CrateNum, mut func: F) -> Vec<T>
98+
where
99+
F: FnMut(DefId) -> Option<T>,
100+
{
101+
if krate == LOCAL_CRATE {
102+
tcx.iter_local_def_id().filter_map(|did| func(did.to_def_id())).collect()
103+
} else {
104+
let num_definitions = tcx.num_extern_def_ids(krate);
105+
(0..num_definitions)
106+
.filter_map(move |i| {
107+
let def_id = DefId { krate, index: rustc_span::def_id::DefIndex::from_usize(i) };
108+
func(def_id)
109+
})
110+
.collect()
111+
}
82112
}
83113

84114
/// Build a stable mir crate from a given crate number.

compiler/stable_mir/src/compiler_interface.rs

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ pub trait Context {
3434
/// Check whether the body of a function is available.
3535
fn has_body(&self, item: DefId) -> bool;
3636
fn foreign_modules(&self, crate_num: CrateNum) -> Vec<ForeignModuleDef>;
37+
38+
/// Retrieve all functions defined in this crate.
39+
fn crate_functions(&self, crate_num: CrateNum) -> Vec<FnDef>;
40+
41+
/// Retrieve all static items defined in this crate.
42+
fn crate_statics(&self, crate_num: CrateNum) -> Vec<StaticDef>;
3743
fn foreign_module(&self, mod_def: ForeignModuleDef) -> ForeignModule;
3844
fn foreign_items(&self, mod_def: ForeignModuleDef) -> Vec<ForeignDef>;
3945
fn all_trait_decls(&self) -> TraitDecls;

compiler/stable_mir/src/lib.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ use serde::Serialize;
2525
use crate::compiler_interface::with;
2626
pub use crate::crate_def::{CrateDef, CrateDefType, DefId};
2727
pub use crate::error::*;
28+
use crate::mir::mono::StaticDef;
2829
use crate::mir::{Body, Mutability};
29-
use crate::ty::{ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty};
30+
use crate::ty::{FnDef, ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty};
3031

3132
pub mod abi;
3233
#[macro_use]
@@ -96,6 +97,16 @@ impl Crate {
9697
pub fn trait_impls(&self) -> ImplTraitDecls {
9798
with(|cx| cx.trait_impls(self.id))
9899
}
100+
101+
/// Return a list of function definitions from this crate independent on their visibility.
102+
pub fn fn_defs(&self) -> Vec<FnDef> {
103+
with(|cx| cx.crate_functions(self.id))
104+
}
105+
106+
/// Return a list of static items defined in this crate independent on their visibility.
107+
pub fn statics(&self) -> Vec<StaticDef> {
108+
with(|cx| cx.crate_statics(self.id))
109+
}
99110
}
100111

101112
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//@ run-pass
2+
//! Test information about crate definitions (local and external).
3+
4+
//@ ignore-stage1
5+
//@ ignore-cross-compile
6+
//@ ignore-remote
7+
//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837
8+
9+
#![feature(rustc_private)]
10+
#![feature(assert_matches)]
11+
12+
extern crate rustc_hir;
13+
#[macro_use]
14+
extern crate rustc_smir;
15+
extern crate rustc_driver;
16+
extern crate rustc_interface;
17+
extern crate stable_mir;
18+
19+
use rustc_smir::rustc_internal;
20+
use stable_mir::CrateDef;
21+
use std::collections::HashSet;
22+
use std::io::Write;
23+
use std::ops::ControlFlow;
24+
25+
const CRATE_NAME: &str = "crate_defs";
26+
27+
/// This function uses the Stable MIR APIs to get information about the test crate.
28+
fn test_stable_mir() -> ControlFlow<()> {
29+
// Find items in the local crate.
30+
let local = stable_mir::local_crate();
31+
check_items(&local.statics(), &["PRIVATE_STATIC", "dummy::PUBLIC_STATIC"]);
32+
check_items(
33+
&local.fn_defs(),
34+
&[
35+
"top_level",
36+
"dummy::public_fn",
37+
"dummy::private_fn",
38+
"dummy::PrivateStruct::new",
39+
"<dummy::PrivateStruct as std::ops::Drop>::drop",
40+
"DummyTrait::method",
41+
"<T as DummyTrait>::method",
42+
],
43+
);
44+
45+
// Find items inside core crate.
46+
// FIXME: We are currently missing primitive type methods and trait implementations for external
47+
// crates.
48+
let core = stable_mir::find_crates("core").pop().expect("Cannot find `core` crate");
49+
contains(
50+
&core.fn_defs(),
51+
&[
52+
"std::fmt::Debug::fmt",
53+
"std::option::Option::<T>::is_some",
54+
"std::ptr::swap",
55+
"<std::slice::Iter<'a, T> as std::iter::Iterator>::next",
56+
"core::num::<impl u8>::abs_diff",
57+
],
58+
);
59+
// Ensure nothing crashes. There is no public static in core that we can test here.
60+
let _ = core.statics();
61+
62+
ControlFlow::Continue(())
63+
}
64+
65+
/// Check if the list of definitions matches the expected list.
66+
/// Note that order doesn't matter.
67+
fn check_items<T: CrateDef>(items: &[T], expected: &[&str]) {
68+
let expected: HashSet<_> = expected.iter().map(|s| s.to_string()).collect();
69+
let item_names: HashSet<_> = items.iter().map(|item| item.name()).collect();
70+
assert_eq!(item_names, expected);
71+
}
72+
73+
/// Check that the list contains the expected items.
74+
fn contains<T: CrateDef + std::fmt::Debug>(items: &[T], expected: &[&str]) {
75+
let expected: HashSet<_> = expected.iter().map(|s| s.to_string()).collect();
76+
let item_names = items.iter().map(|item| item.name()).collect();
77+
let not_found: Vec<_> = expected.difference(&item_names).collect();
78+
assert!(not_found.is_empty(), "Missing items: {:?}", not_found);
79+
}
80+
81+
/// This test will generate and analyze a dummy crate using the stable mir.
82+
/// For that, it will first write the dummy crate into a file.
83+
/// Then it will create a `StableMir` using custom arguments and then
84+
/// it will run the compiler.
85+
fn main() {
86+
let path = "crate_definitions.rs";
87+
generate_input(&path).unwrap();
88+
let args = vec![
89+
"rustc".to_string(),
90+
"--crate-type=lib".to_string(),
91+
"--crate-name".to_string(),
92+
CRATE_NAME.to_string(),
93+
path.to_string(),
94+
];
95+
run!(args, test_stable_mir).unwrap();
96+
}
97+
98+
fn generate_input(path: &str) -> std::io::Result<()> {
99+
let mut file = std::fs::File::create(path)?;
100+
write!(
101+
file,
102+
r#"
103+
#![allow(dead_code, unused_variables)]
104+
static PRIVATE_STATIC: u8 = 0;
105+
fn top_level() -> &'static str {{
106+
"hello"
107+
}}
108+
109+
pub trait DummyTrait {{
110+
fn method(&self) -> Self;
111+
}}
112+
113+
impl<T: Copy> DummyTrait for T {{
114+
fn method(&self) -> T {{
115+
*self
116+
}}
117+
}}
118+
119+
pub mod dummy {{
120+
pub static mut PUBLIC_STATIC: Option<char> = None;
121+
122+
pub fn public_fn(input: bool) -> bool {{
123+
private_fn(!input)
124+
}}
125+
126+
fn private_fn(input: bool) -> bool {{
127+
todo!()
128+
}}
129+
130+
struct PrivateStruct {{
131+
field: u32,
132+
}}
133+
134+
impl PrivateStruct {{
135+
fn new() -> Self {{
136+
Self {{ field: 42 }}
137+
}}
138+
}}
139+
140+
impl Drop for PrivateStruct {{
141+
fn drop(&mut self) {{
142+
println!("Dropping PrivateStruct");
143+
}}
144+
}}
145+
}}
146+
"#
147+
)?;
148+
Ok(())
149+
}

0 commit comments

Comments
 (0)