LoginSignup
1
0

Rustのコンパイラと戯れる 〜 2日目

Last updated at Posted at 2024-04-02

はじめに

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

上のコードは、structimplの構造を抽出している。

  • structrustc_hir::ItemKind::Struct(...) から得ることができる。
    • ここでは型名だけ取り出している
  • implrustc_hir::ItemKind::Impl(...) から得ることができる。
    • ItemKind::Implのvariantitemsフィールドを持ち、ここに定数、型定義、関数名が保持されている。
    • itemsrustc_hir::ImplItemRefの配列であるが、この値は大した情報を持っておらず、実態は別の場所で保持されている。
      • ImplItemRefidを引数にrustc_middle::hir::map::Map::impl_itemメソッドを呼ぶことで、定義の実体を取得できる。
      • 上記のコードでは、ここから関数名、引数、戻り値を取得している。

以下に(長いので)実行結果の一部を抜粋する。

[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日目書いた。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0