はじめに
1日目では、ソース本文をビルドして、得られた構造を眺めた。
今日は、ソースファイルのパスを渡してコンパイルさせ、より詳細に構造を眺めたい。
例として、std::time
モジュールが記述された、time.rs
を使用する。
実装
いつもどおりプロジェクトを作成し、以下のコードを貼り付ける。
コード(長いので折りたたみ)
#![feature(rustc_private)]
extern crate rustc_interface;
extern crate rustc_session;
extern crate rustc_hir;
extern crate rustc_hash;
extern crate rustc_errors;
extern crate rustc_driver;
extern crate rustc_middle;
use rustc_interface::interface;
use rustc_session::config;
use rustc_hash::{FxHashMap};
use rustc_hir::ItemKind;
use rustc_hir::ImplItemKind;
use rustc_hir::FnRetTy;
use rustc_hir::FnSig;
use rustc_hir::TyKind;
use rustc_hir::QPath;
use std::path::PathBuf;
fn main() {
let rustc_out = std::process::Command::new("rustc")
.arg("--print=sysroot")
.current_dir(".")
.output()
.unwrap()
;
let sysroot = String::from_utf8(rustc_out.stdout).unwrap().trim().to_string();
let file_path: PathBuf = [
&sysroot,
"lib/rustlib/src/rust/library/std/src/time.rs"
]
.iter().collect();
let using_internal_features = rustc_driver::install_ice_hook(
"https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md",
|_| (),
);
// println!("{:?}", file_path);
let config = interface::Config {
opts: config::Options {
maybe_sysroot: Some(PathBuf::from(sysroot)),
..config::Options::default()
},
input: config::Input::File(file_path),
registry: rustc_driver::diagnostics_registry(),
locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES,
lint_caps: FxHashMap::default(),
crate_cfg: vec![],
crate_check_cfg: vec![],
expanded_args: vec![],
output_dir: None,
output_file: None,
file_loader: None,
register_lints: None,
override_queries: None,
hash_untracked_state: None,
ice_file: None,
make_codegen_backend: None,
psess_created: None,
using_internal_features,
};
interface::run_compiler(config, |compiler| {
compiler.enter(|queries| {
let Ok(mut gcx) = queries.global_ctxt() else { rustc_errors::FatalError.raise() };
gcx.enter(|ctx| {
for item_id in ctx.hir().items() {
let item = ctx.hir().item(item_id);
let hir_id = item_id.hir_id();
let def_id = hir_id.owner.def_id.to_def_id();
// println!("{:?}\n", &item.kind);
match item.kind {
ItemKind::Struct(_, _) => {
// let ty = ctx.type_of(def_id).instantiate_identity();
println!("[ItemKind::Struct]");
println!("def_id: {:?}", def_id);
println!("typedef: {:?}", ctx.item_name(def_id));
println!("~~~~~~~~~~");
}
ItemKind::Impl(impl_def) if impl_def.items.len() > 0 => {
println!("[ItemKind::TraitItemRef] (len: {:?}, of_trait? {})", impl_def.items.len(), if impl_def.of_trait.is_none() {"self"} else {"impl"});
println!("self_ty: ");
walk_type(&ctx, impl_def.self_ty, 4);
for item in impl_def.items {
println!("-----");
walk_impl_item(&ctx, &item);
}
println!("~~~~~~~~~~");
}
ItemKind::Mod(_) => println!("[ItemKind::Module] def_id: {:?}", def_id),
_ => {}
}
// println!("item: {:?}", item.kind);
}
})
});
});
}
fn walk_impl_item(ctx: &rustc_middle::ty::TyCtxt, item: &rustc_hir::ImplItemRef) {
let impl_item = ctx.hir().impl_item(item.id);
match impl_item.kind {
ImplItemKind::Fn(FnSig{ header, decl, .. }, _) => {
println!("def_id: {:?}", item.id.owner_id.def_id);
println!("fn.name: {:?}", impl_item.ident.name);
println!("fn.sig.header: {:?}", header);
match decl.output {
FnRetTy::Return(ty) => {
println!("fn.return:");
walk_type(ctx, &ty, 4);
}
FnRetTy::DefaultReturn(_) => println!("fn.return: (none)")
}
println!("fn.self: {:?}", decl.implicit_self);
for (i, p) in decl.inputs.iter().enumerate() {
println!("fn.param[{}]: ", i);
walk_type(ctx, &p, 4);
}
}
ImplItemKind::Const(_, _) => {
println!("[SKIP] Impl Const");
}
ImplItemKind::Type(_) => {
println!("[SKIP] Impl Type");
}
}
}
fn walk_type(ctx: &rustc_middle::ty::TyCtxt, ty: &rustc_hir::Ty, indent: usize) {
println!("{:indent$}id = {:?}", "", ty.hir_id, indent = indent);
match ty.kind {
TyKind::Path(QPath::Resolved(Some(mut_ty), rustc_hir::Path {res, ..})) => {
println!("{:indent$}kind = Path#1, name = {}", "", ctx.hir().name(ty.hir_id), indent = indent);
walk_type(ctx, mut_ty, indent + 4);
println!("{:indent$}res = {:?}", "", res, indent = indent);
}
TyKind::Path(QPath::Resolved(None, rustc_hir::Path {res, segments, ..})) => {
println!("{:indent$}kind = Path#2", "", indent = indent);
println!("{:indent$}res = {:?}", "", res, indent = indent+4);
for (i, seg) in segments.iter().enumerate() {
println!("{:indent$}seg[{}]", "", i, indent = indent+4);
println!("{:indent$}id = {:?}", "", seg.hir_id, indent = indent+8);
println!("{:indent$}name = {:?}", "", ctx.hir().name(seg.hir_id), indent = indent+8);
println!("{:indent$}args = {:?}", "", seg.args, indent = indent+8);
}
}
TyKind::Ref(_, mut_ty) => {
println!("{:indent$}kind = Ref", "");
walk_type(ctx, mut_ty.ty, indent + 4);
println!("{:indent$}mut = {:?}", "", mut_ty.mutbl, indent = indent+4);
}
TyKind::Tup(tup_items) => {
println!("{:indent$}kind = Tup, len = {}", "", tup_items.len());
for ty in tup_items {
walk_type(ctx, ty, indent + 4);
}
}
TyKind::InferDelegation(_, _) => println!("{:indent$}[SKIP] InferDelegation", "", indent = indent),
TyKind::Slice(_) => println!("{:indent$}[SKIP] Slice", "", indent = indent),
TyKind::Array(_, _) => println!("{:indent$}[SKIP] Array", "", indent = indent),
TyKind::Ptr(_) => println!("{:indent$}[SKIP] Ptr", "", indent = indent),
TyKind::BareFn(_) => println!("{:indent$}[SKIP] BareFn", "", indent = indent),
TyKind::Never => println!("{:indent$}[SKIP] Never", "", indent = indent),
TyKind::AnonAdt(_) => println!("{:indent$}[SKIP] AnonAdt", "", indent = indent),
TyKind::OpaqueDef(_, _, _) => println!("{:indent$}[SKIP] OpaqueDef", "", indent = indent),
TyKind::TraitObject(_, _, _) => println!("{:indent$}[SKIP] TraitObject", "", indent = indent),
TyKind::Typeof(_) => println!("{:indent$}[SKIP] Typeof", "", indent = indent),
TyKind::Infer => println!("{:indent$}[SKIP] Infer", "", indent = indent),
TyKind::Err(_) => println!("{:indent$}[SKIP] Err", "", indent = indent),
TyKind::Path(p) => println!("{:indent$}[SKIP] another Path {:?}", "", p, indent = indent),
}
}
上のコードは、struct
とimpl
の構造を抽出している。
-
struct
はrustc_hir::ItemKind::Struct(...)
から得ることができる。- ここでは型名だけ取り出している
-
impl
はrustc_hir::ItemKind::Impl(...)
から得ることができる。- ItemKind::Implの
variant
はitems
フィールドを持ち、ここに定数、型定義、関数名が保持されている。 -
items
はrustc_hir::ImplItemRef
の配列であるが、この値は大した情報を持っておらず、実態は別の場所で保持されている。-
ImplItemRef
のid
を引数にrustc_middle::hir::map::Map::impl_item
メソッドを呼ぶことで、定義の実体を取得できる。 - 上記のコードでは、ここから関数名、引数、戻り値を取得している。
-
- ItemKind::Implの
以下に(長いので)実行結果の一部を抜粋する。
[ItemKind::Struct]
def_id: DefId(0:69 ~ time[58d1]::Instant)
typedef: "Instant"
~~~~~~~~~~
[ItemKind::TraitItemRef] (len: 1, of_trait? impl)
self_ty:
id = HirId(DefId(0:73 ~ time[58d1]::{impl#19}).5)
kind = Path#2
res = Def(Struct, DefId(0:69 ~ time[58d1]::Instant))
seg[0]
id = HirId(DefId(0:73 ~ time[58d1]::{impl#19}).6)
name = "Instant"
args = None
-----
def_id: DefId(0:74 ~ time[58d1]::{impl#19}::clone)
fn.name: "clone"
fn.sig.header: FnHeader { unsafety: Normal, constness: NotConst, asyncness: NotAsync, abi: Rust }
fn.return:
id = HirId(DefId(0:74 ~ time[58d1]::{impl#19}::clone).23)
kind = Path#2
res = Def(Struct, DefId(0:69 ~ time[58d1]::Instant))
seg[0]
id = HirId(DefId(0:74 ~ time[58d1]::{impl#19}::clone).24)
name = "Instant"
args = None
fn.self: RefImm
fn.param[0]:
id = HirId(DefId(0:74 ~ time[58d1]::{impl#19}::clone).22)
kind = Ref
id = HirId(DefId(0:74 ~ time[58d1]::{impl#19}::clone).21)
kind = Path#2
res = SelfTyAlias { alias_to: DefId(0:73 ~ time[58d1]::{impl#19}), forbid_generic: false, is_trait_impl: true }
seg[0]
id = HirId(DefId(0:74 ~ time[58d1]::{impl#19}::clone).20)
name = "Self"
args = None
mut = Not
~~~~~~~~~~
(snip)
[ItemKind::TraitItemRef] (len: 7, of_trait? self)
self_ty:
id = HirId(DefId(0:16 ~ time[58d1]::{impl#0}).1)
kind = Path#2
res = Def(Struct, DefId(0:69 ~ time[58d1]::Instant))
seg[0]
id = HirId(DefId(0:16 ~ time[58d1]::{impl#0}).2)
name = "Instant"
args = None
-----
def_id: DefId(0:17 ~ time[58d1]::{impl#0}::now)
fn.name: "now"
fn.sig.header: FnHeader { unsafety: Normal, constness: NotConst, asyncness: NotAsync, abi: Rust }
fn.return:
id = HirId(DefId(0:17 ~ time[58d1]::{impl#0}::now).11)
kind = Path#2
res = Def(Struct, DefId(0:69 ~ time[58d1]::Instant))
seg[0]
id = HirId(DefId(0:17 ~ time[58d1]::{impl#0}::now).12)
name = "Instant"
args = None
fn.self: None
-----
(snip)
deriveマクロの適用結果のような、ファイル上には現れない情報も取得できていることがわかる。
正気を保っていることができたら、3日目へ続く・・・
2024-04-12追記
3日目書いた。