Using the rustc_interface crate

I'm trying to use rustc_interface to interact with the compiler, as described in https://rustc-dev-guide.rust-lang.org/rustc-driver.html

But I'm unable to find an explanation or example of how to access the rustc_interface crate. It doesn't seem to be published anywhere. What is the recommended way for an external tool to use rustc_interface?

I think you just need to use nightly and add extern crate rustc_interface, and you may need to add some feature gate or special attribute...

Basically, rustc_interface (and all of the rustc_*) crates are in the sysroot by default, but using them requires special "I know what I'm doing" incantations.

OK, I'm on nightly, and I'm trying to compile this:

#[allow(unused_extern_crates)]
extern crate rustc_interface;

use self::rustc_interface::interface;

fn main() {
    let config = interface::Config{};
    interface::run_compiler(config, |compiler| {
        println!("Hello, world!");
    });
}

with rustc src/main.rs, but I'm still getting the error:

error[E0463]: can't find crate for `rustc_interface`
 --> src/main.rs:2:1
  |
2 | extern crate rustc_interface;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate

error: aborting due to previous error

and cargo build produces the same error.

Hmm... it seems my info is out of date. Sorry... I'm honestly not sure how to do this now.

I will be happy to publish a "rustc_interface hello world" github repo if someone who understands the internals can help me figure out how to do this...

I am making progress...it seems that these components were removed from nightly by default and now you need to do rustup component add --toolchain nightly rustc-dev to get access to them.

1 Like

Using rustc_interface may require installing the rustc-dev component through rustup.

1 Like

OK, I have a basic example working: https://github.com/georgewfraser/hello_rustc

If any rustc experts see room for improvement in that example, please comment or make a PR!

1 Like

So I am able to parse successfully, but when I try to run the query lower_to_hir: https://github.com/georgewfraser/hello_rustc/blob/c503f5bc96c6966c777de43d5aebdbb77bbe8635/src/main.rs#L79

I get the error message:

error[E0463]: can't find crate for `std`

I'm guessing I need to change some of the options in rustc_interface::interface::Config but there doesn't seem to be any documentation and the way rustc itself uses Config is hard to follow. Perhaps someone can point me in the right direction?

You might need to run expansion (the expansion query) after HIR lowering. IIRC, expansion inserts an implicit extern crate core; extern crate std; at the top of the crate.

Not sure if this is helpful, but you can see how the compiler uses the interface here: https://github.com/rust-lang/rust/blob/45ebd5808afd3df7ba842797c0fcd4447ddf30fb/src/librustc_driver/lib.rs#L147-L431

Specifically, note line 299 which does expansion after calling global_ctxt, which forces lower_to_hir.

The error is happening when I call lower_to_hir, so it's hard to see how calling expansion after I call lower_to_hir is going to fix the error that already happened. But I tried making the order of operations more similar to the compiler:

interface::run_compiler(config, |compiler| {
    let sess = compiler.session();
    compiler.enter(|queries| {
        queries.parse().unwrap();
        println!("ran parse");
        queries.register_plugins().unwrap();
        println!("ran register_plugins");
        queries.global_ctxt().unwrap();
        println!("ran global_ctxt");
        queries.expansion().unwrap();
        println!("ran expansion");
    });
});

And I get the same error, after ran register_plugins is printed. I also tried just running parse and expansion:

interface::run_compiler(config, |compiler| {
    let sess = compiler.session();
    compiler.enter(|queries| {
        queries.parse().unwrap();
        println!("ran parse");
        queries.expansion().unwrap();
        println!("ran expansion");
    });
});

Same error.

You may want to pass a --sysroot argument with a value equal to the result of rustc --print sysroot.

Thanks @bjorn3! My example is in pretty good shape, hopefully this will be helpful to anyone else trying to do the same thing. https://github.com/georgewfraser/hello_rustc

3 Likes

Would you be interested in updating the rustc-dev-guide chapter you pointed to in the OP?

Done https://github.com/rust-lang/rustc-dev-guide/pull/621

2 Likes

So now that we're merrily improving the rustc docs, I have a second question: how can I get the type of a span that isn't a definition? Suppose I want to get the type of message inside println("{}", message); in the below program:

fn main() {
  let message = "Hello, world!";
  println("{}", message);
}

Complete example here: https://gist.github.com/georgewfraser/2dc8af432b4766749ee51d32844b8e20#file-main-rs-L86

(I am happy to continue submitting PRs with additions to the docs, if the experts will keep answering my noob questions)

You can do this in different ways: you could traverse the AST before or after expansion (before is easier to recognize the println) or you could traverse the HIR. The span should then be stored next to the variable use.

https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.Expr.html

https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Expr.html

Ok thank you, but how do I get from the span to the type? I'm not seeing any Span -> Ty function in TyCtxt or Queries, the closest thing is type_of: DefId -> Ty, which only seems to make sense for getting types top-level definitions.

First use https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/struct.TyCtxt.html#method.typeck_tables_of with the DefId of the current function, then call https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/struct.TypeckTables.html#method.node_type on the result with the HirId of the expression as argument I think.

Thanks @bjorn3, I've added an example to the docs: https://rustc-dev-guide.rust-lang.org/rustc-driver-interacting-with-the-ast.html

Now I'm trying to figure out how to get compiler error messages. I'm guessing I need to use the global_ctx() query to get a TyCtxt, similar to how I get the type of an expression, but it's not clear where I go for a list of errors. Can you point me in the right direction?

1 Like