gcc has a similar stage1/stage2 split. Stage1 uses standard C (no gcc extensions) and so is reasonably portable. The stage1 compiler is used to build stage2, libgcc, etc - so all that can assume full gcc features (but still no floating point emulation and other support that comes in later with stage2+libgcc).
I think the equivalent in Rust would be stage1 restricting itself to some widely available subset of language features (I suggested “stable” above); and stage2 being able to use all compiler features, but not libstd and friends.
Re packaging: Debian has a somewhat overly-restrictive policy of always building packages on the native architecture (not cross-compiling). When bootstrapping a new architecture, this gets overlooked in order to break the C compiler dependency cycle - a cross-compiler from an existing architecture or a toolchain found outside Debian (yocto is a popular choice) is used to build the first gcc package and the small set of “build-essential” packages. The build-essential (including gcc) packages are rebuilt on the native arch before uploading to the Debian archive - to ensure consistency with future rebuilds. Once these are available, the new architecture can (theoretically) build everything else.
For Rust packaging, we have two variations of the exact same bootstrapping problem, with the exact same solutions: Bootstrapping a new architecture needs a cross-compiler similar to above, and bootstrapping Rust itself can use a Rust compiler provided outside Debian.
This leaves us with “only” the problem of maintaining the status-quo on an existing architecture - and you can see that I’m looking for a way to build the next compiler revision using an existing compiler without having to resort to a stage0 opaque blob I found on the internet every time. At the moment, I’m considering patching in my own unofficial support for language version attributes, using it to replace a bunch of the cfg(stage0) guards, and see how far I can stretch the specific version of stage0 required for stage1.