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

7 files changed

Lines changed: 225 additions & 4 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
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‎

Lines changed: 10 additions & 0 deletions
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‎

Lines changed: 15 additions & 1 deletion
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
usecrate::rustc_internal::RustcInternal;
3636
usecrate::rustc_smir::builder::BodyBuilder;
37-
usecrate::rustc_smir::{Stable,Tables, alloc, new_item_kind, smir_crate};
37+
usecrate::rustc_smir::{Stable,Tables, alloc,filter_def_ids,new_item_kind, smir_crate};
3838

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

83+
fncrate_functions(&self,crate_num:CrateNum) -> Vec<FnDef>{
84+
letmut 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+
fncrate_statics(&self,crate_num:CrateNum) -> Vec<StaticDef>{
91+
letmut 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
fnforeign_module(
8498
&self,
8599
mod_def: stable_mir::ty::ForeignModuleDef,

‎compiler/rustc_smir/src/rustc_smir/mod.rs‎

Lines changed: 32 additions & 2 deletions
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+
fnto_fn_def(&mutself,def_id:DefId) -> Option<FnDef>{
84+
ifmatches!(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+
fnto_static(&mutself,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)fnfilter_def_ids<F,T>(tcx:TyCtxt<'_>,krate:CrateNum,mutfunc: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‎

Lines changed: 6 additions & 0 deletions
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
fnhas_body(&self,item:DefId) -> bool;
3636
fnforeign_modules(&self,crate_num:CrateNum) -> Vec<ForeignModuleDef>;
37+
38+
/// Retrieve all functions defined in this crate.
39+
fncrate_functions(&self,crate_num:CrateNum) -> Vec<FnDef>;
40+
41+
/// Retrieve all static items defined in this crate.
42+
fncrate_statics(&self,crate_num:CrateNum) -> Vec<StaticDef>;
3743
fnforeign_module(&self,mod_def:ForeignModuleDef) -> ForeignModule;
3844
fnforeign_items(&self,mod_def:ForeignModuleDef) -> Vec<ForeignDef>;
3945
fnall_trait_decls(&self) -> TraitDecls;

‎compiler/stable_mir/src/lib.rs‎

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

3132
pubmod abi;
3233
#[macro_use]
@@ -96,6 +97,16 @@ impl Crate {
9697
pubfntrait_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+
pubfnfn_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+
pubfnstatics(&self) -> Vec<StaticDef>{
108+
with(|cx| cx.crate_statics(self.id))
109+
}
99110
}
100111

101112
#[derive(Copy,Clone,PartialEq,Eq,Debug,Hash,Serialize)]
Lines changed: 149 additions & 0 deletions
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+
externcrate rustc_hir;
13+
#[macro_use]
14+
externcrate rustc_smir;
15+
externcrate rustc_driver;
16+
externcrate rustc_interface;
17+
externcrate 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+
constCRATE_NAME:&str = "crate_defs";
26+
27+
/// This function uses the Stable MIR APIs to get information about the test crate.
28+
fntest_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+
fncheck_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+
fncontains<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+
fnmain(){
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+
fngenerate_input(path:&str) -> std::io::Result<()>{
99+
letmut 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)