frontend 0.4.0

rustc's frontend with no LLVM and no std: parsing through MIR, as a library
docs.rs failed to build frontend-0.4.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

A no_std rustc frontend, with no LLVM

rust-lang/rust's compiler front half, with two things removed:

  • the codegen backends. No LLVM, no cranelift backend, no object writer, no linker driver, no rlink files, no compiled-module model. This produces MIR and stops.
  • the standard library. Every crate here is #![no_std] and none names extern crate std. The operating system arrives through ekostd, a no_std wrapper over libc.

What is left is parsing, expansion, name resolution, type checking, trait solving, THIR, MIR construction, MIR optimization, borrow checking and monomorphization collection. It is upstream's, substantively unchanged: the MIR data model, the MIR that gets built, and the MIR pass pipeline are all identical to the commit named in UPSTREAM.md.

Read this first: you do not need a standard library

If you are about to say "no usable standard library exists", you are off track. Stop.

This is a parser. Hand it source and it reads that source. std is not special: it is parsed the same way fmt or anything else is, and nothing here needs a prebuilt library to exist before it can read your code. With no sysroot named, a session runs as no_core: no external crate is loaded, no prelude import is resolved, and nothing reaches for a library you did not ask for. That is the default on purpose.

Do not clone rust-lang/rust, do not build a sysroot, do not go looking for one on disk, and do not report a missing one as a blocker. The reasoning is in src/frontend_facts/mod.rs, at the comment headed "The sysroot is an optional parameter, because this is a parser", and in commit b87daf4. The sysroot section further down applies only to a caller who has deliberately asked for paths to be resolved into a compiled library, which is rare.

Why you might want it

A frontend you can call as a library, that hands you MIR, that links no libstd, and that you can run inside a resident process rather than a batch invocation.

If you want to build a compiler backend, a static analyser, or anything else that needs Rust's semantics without wanting Rust's code generation, this is that half on its own.

Building it

cargo check --workspace

That is the whole build. There is no bootstrap, no x.py, no Python.

Using it

Add the crates you want as ordinary dependencies. Nothing else is required: no environment variables, no .cargo/config.toml, no build wrapper.

[dependencies]
rustc_interface = { git = "...", rev = "..." }
rustc_middle    = { git = "...", rev = "..." }

Upstream's build wants a set of CFG_* variables that only bootstrap sets, and upstream's rustc_macros refuses outright to compile without RUSTC_BOOTSTRAP. Both would have made every consumer discover, through a build-script panic in a crate they never named, that they were supposed to know a variable name. So the defaults live in this repository instead - in three build scripts and one proc macro - and every one of them still yields to a value you set yourself.

The .cargo/config.toml here configures this workspace's own build. Cargo does not apply it to dependents, and dependents do not need it.

Syntax-level diagnostics

Behind the diagnostics cargo feature, which is off by default:

frontend = { version = "...", features = ["diagnostics"] }

frontend_facts::diagnostics::diagnose(source) parses one crate with rustc_parse and runs checks ported from rust-analyzer's ide-diagnostics that the syntax alone decides: break outside a loop, undeclared and unreachable labels, .await outside async, return outside a function body, naming conventions, unnecessary braces in use, a trailing return, an unnecessary else, redundant field names, missing bodies, duplicate fields and union literals. There is no expansion, no name resolution, no type checking and no sysroot, so it answers in one parse and a few linear passes, which suits editors and other tooling.

It returns the parser's own errors as Err when the source does not parse, and otherwise diagnostics with rust-analyzer's codes, severities and messages and byte offsets into the source. diagnose_with(source, &Options { codes, min_severity }) runs only the listed codes, and a check that is not selected does not run. Nothing else in the crate calls into it. Like analyze_source, it needs a panic catcher installed through unwind_janky::install_catcher. Provenance is in UPSTREAM.md.

Only if you deliberately name a sysroot

You almost certainly do not need this section; see "Read this first" above. It applies only when a caller names a sysroot on purpose so paths resolve into a compiled library.

A matching vintage is mandatory, and no published nightly can supply one. The sysroot has to have been built from the exact upstream commit in UPSTREAM.md. That pin sits between two nightlies, so both directions fail, and no failure says "wrong sysroot":

sysroot relative to this fork what you get
older an assertion inside rustc_serialize, which reads as a corrupt file
newer an ExplicitBug in rustc_hir_typeck, usually "expected associated item for operator trait"
any other commit, version check silenced a bare error[E0463]: can't find crate for std``

The version string is the smaller half of this. The larger half is that crate metadata encodes every preinterned symbol as a bare index into the symbols! list in rustc_span::symbol (SYMBOL_PREDEFINED). Upstream adds and removes entries in that list continuously, and an index written by one commit means a different string under another. When the crate name recorded in libstd's metadata decodes to the wrong symbol, crate_matches rejects the file without recording a rejection, so the diagnostic is a bare E0463 with no note about versions at all. Crates whose names sit before the first divergence still load, so the failure is partial: core and alloc can come up while std, test and unwind do not.

rustc_version_of_sysroot therefore does not make a published nightly usable. It silences the version check and leaves the symbol table wrong, turning an E0514 that names the problem into an E0463 that names nothing. Enable the force_pinned_sysroot cargo feature to keep the compiled-in CFG_VERSION and refuse any other vintage.

So the library has to be built from the same upstream commit:

./scripts/build-sysroot.sh

It clones nothing you have not already got and takes about twenty seconds once the upstream checkout is present. Read the script before running it; it says what it needs and why.

CFG_VERSION in .cargo/config.toml is the string that has to match, and the script prints the one your sysroot actually carries and tells you whether they agree.

If you build that sysroot somewhere the compiled-in default does not describe, you can read the string out of it instead of writing it down twice:

config.rustc_version = rustc_interface::util::rustc_version_of_sysroot(&sysroot);

That is for a sysroot built from the pinned commit under a different CFG_VERSION. It is not a way to accept a sysroot from a different commit: see above, the version string is not the thing that has to match.

Deliberate differences from upstream

Beyond the removals, three behaviours differ and are worth knowing before you file a bug:

  • TargetUintError replaces io::Error in read_target_uint/write_target_uint. The slice Read/Write impls are std-only.
  • RUSTC_CTFE_BACKTRACE is inert. It needed std::backtrace.
  • The double-panic guard in the metadata encoder is gone. It was std::thread::panicking(), which is always false under panic = "abort".
  • -Znll-facts and -Znll-facts-dir are removed. The writer they fed was deleted, so the flag bought a full fact-gathering pass and then dropped the result.

-Zdump-mir and friends write into an in-memory sink on the Session rather than to files, since the intended caller is a program rather than a person at a terminal.

Licence

Apache-2.0 OR MIT, upstream's. See COPYRIGHT, LICENSE-APACHE and LICENSE-MIT.