Disabling -Z for stable builds is a mistake

https://github.com/rust-lang/rust/pull/41751 disables -Z options for stable builds of rustc. I think this is a mistake.

I’m completely on board with the Rust project’s handling of stability of the language itself, and how rustc implements the stability rules.

But the concept of stability for rustc-the-tool is a completely separate thing, and I think it’s a mistake to try and extend the concepts of language stability to rustc. I think trying to extend the concept of “stability” to rustc improperly conflates the language with its implementation.

None (that I could see) of the options in -Z change the language rustc compiles, so they don’t have any impact on language stability. They’re primarily concerned with debugging, stats or changing the properties of the generated code in various ways (sanitizers, path remapping, etc).

I’m happy with options under -Z being defined as “unstable” in that users should not expect them to continue working, continue to exist, or produce the same output. But by making them unavailable to stable builds means that they also can’t be used to either debug performance problems observed in practice, or to try out experimental features in a production environment.

If I deploy an unstable compiler to production to make -Z features available, then that also means I’m opening up access to unstable language features, which I don’t want.

So I’d like to propose that 41751 is reverted, and we retain the current behaviour of allowing -Z for stable builds, albeit with no guarantees and with annoying warnings.

10 Likes

Which -Z features do you use for debugging? Is there a middle-ground where we can stabilize some heavily-used flags while still continuing with our plans to lockdown the compiler interface?

The problem that we are concerned about is more about tooling. If someone has (say) a build.rs script that relies on -Z plugins to do "something or other", and we change some details of this option, and then your library doesn't build any more -- that is still going to be frustrating. Similarly if you are using something in your personal toolchain.

An example of this is that we now support --emit mir; the output includes a caveat designed to make it clear that, while the ability to dump mir will remain, the format of that MIR is not remotely stable:

// WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.

I frequently use trace-macros and unstable-options --pretty=expanded, specifically for debugging.

9 Likes

The problem that we are concerned about is more about tooling. If someone has (say) a build.rs script that relies on -Z plugins to do "something or other", and we change some details of this option, and then your library doesn't build any more -- that is still going to be frustrating. Similarly if you are using something in your personal toolchain.

Yes, I agree that's a problem. I like that all the "unstable" options are behind -Z and even OK with it printing a loud message every time you use them. I'd even be OK with requiring a -Z I-insertnamehere-being-of-sound-mind-understand-that-this-could-break-at-any-time-in-any-future-version option to make them available.

(I've talked at length about my problems with build.rs elsewhere, but it would be a very bad idea for build.rs to ever directly invoke rustc in any case.)

But by disabling them altogether, means that when options like -Zremap-path-prefix-from/to appear in a stable build, I won't be able to experiment to see if they work as I'd like when they are finally stabilized. I'd need to have a whole separate rustc version which is a lot more effort to start with which means the threshold for doing the experiment at all is a lot higher (which means less likely to happen), and changes a lot of other things as well (like the accepted language, which may in turn require code changes).

Which -Z features do you use for debugging? Is there a middle-ground where we can stabilize some heavily-used flags while still continuing with our plans to lockdown the compiler interface?

The options I've been playing with lately are -Zsanitizer= and I'd like to play with -Zremap-path-prefix-from/to once its in a stable build. Once I've deployed a new stable toolchain into our infra (just landed 1.17), if I have a spare moment I like to play with experimental features to see how I can make use of them. This is typically local changes to various bits of scripts and tools, which makes it all fairly low effort and low friction, which goes away if I have to include "use different compiler" in the mix.

(If anything, it would be more useful for me if rustbuild had a guaranteed-stable interface so that my local build scripts "just work" from version to version without changes (to be fair, it isn't too bad aside from some missing features).)

rustc should certainly have a guaranteed-stable set of functionality. But I don't see any value in making rustc have only a stable interface.

I'd also happily concede:

  • the stable compiler shouldn't have any options which changes the language it compiles, and
  • tools like cargo and/or crates.io should reject build specs that include -Z options
2 Likes

--print target-spec-json

Are you not able to experiment on a nightly build? (That is what I would expect to happen.)

Ah, well, I guess you posited that this is a higher barrier to entry. I guess I expect workflows to be optimized for allowing either nightly or stable, in part because testing on stable helps you find "upcoming" bugs in the compiler.

Ah, well, I guess you posited that this is a higher barrier to entry.

Right. I'd need to work out how to deploy parallel stable and nightly toolchains to the build farm, and work out how to get it to choose one or the other.

1 Like

FYI: we’ve added this concern to the triage list for the next core team triage, just in case we can’t reach a resolution on this thread in the near future.

2 Likes

Can we detect if rustc is run from your shell instead of from any tool or script? For example parent is sh/bash/zsh/… its parent is not sh/bash/zsh/… and it doesn’t have any arguments.

Interactive use is usually detected by isatty().

Forgot that one :frowning:

Please don’t take away -Zdebug-macros on the release channel without either making it the default (my preference but I realize unlikely to happen) or introducing a release-channel flag for it.

-Zdebug-macros is needed for debugging code expanded from macros, for getting useful panic/crash reporting both for testing and from deployments in the field and for correct attribution when profiling. All these things should be possible in executables built with release-channel Rust.

1 Like

I use -Zparse-only with syntastic in vim to get very quick feedback on whether I’ve committed a syntax error.

1 Like

Isn’t -Zparse-only now covered by cargo check? If syntastic isn’t using that yet, why not?

cargo check does semantic analysis as well as syntax checking

We discussed this issue in the core team meeting today, revisiting the rationale and history. I'm going to try to document the high-level sentiments that came out, and we can continue from there.

Note also that we've been issuing strong deprecation warnings about this change since Rust 1.8, for over a year.

Philosophical issues

I think this is already a key point of disagreement. The stability story has always been about the compiler:

If your code compiles on Rust stable 1.0, it should compile with Rust stable 1.x with a minimum of hassle.

This is a crucial component to the success of the train/rapid release model: we want users to treat toolchain upgrades as a straightforward, frequent thing, which helps move the whole ecosystem forward, helps deprecations to work, and so on. (We will eventually want to offer some "long-term support" releases for folks who want to be more conservative, but the point still stands.) Given that desire, it's critical that any given toolchain upgrade causes minimal hassle, and that's the heart of our stability promise.

More broadly, Rust's design philosophy is to take a holistic perspective: we try to avoid creating silos around std vs the language vs the compiler vs Cargo vs other tools vs the ecosystem, and instead focus on the quality of the end-to-end user experience for Rust. That's another reason that the approach to stability extends beyond the pure "language definition" sense.

Coming from this philosophical standpoint, it's paramount that compiler flags, Cargo.toml format, and other similar details follow the same pattern: if you can use them on the stable release channel, they will work in a stable fashion, full stop.

Practical issues

Turning to the more practical issues raised later in the thread:

This exact argument can be made about unstable language or library features as well. It is a definite downside of our stability model -- we get less testing of unstable features because they are usable only on nightly. But that is an inherent aspect of what we're trying to achieve with the stability system: if these things were easy to opt into, then de facto stability becomes a much more likely outcome, as this very thread indicates! In other words, this is a tradeoff we've already bought into, and it's hard to see a compelling case as to why flags are particularly different here than being able to test out language features (which could also have build system interactions).

In terms of changing the accepted language, this is one reason that using nightly doesn't just enable all unstable features freely -- it still requires granular opt-in. Thus, it's easy to catch and prevent any divergences.

I'm not sure what else to add here. It's definitely a case of turning down a request that would ease a particular workflow, because of the long-term, broader impact on the ecosystem. But stability is something we've taken very seriously, and tried to provide a lot of clarity on, in the form of:

  • If it compiles on the stable toolchain, it will continue to compile on new versions thereof, modulo minor disambiguations (like type annotations).

It's hard to see a compelling argument to step away from this stance for flags.

Thanks for taking the time to reconsider and respond to this, even if the outcome is not the one I want.

Since I'm already committed to private builds of the toolchain, the best compromise here would be the ability to build rustc from a stable version as a nightly at least WRT the command line, but still disallow the use of #[feature(...)] to prevent language/library-level extensions.

I have more specific comments, but I'm not sure they really add much, and I don't want to relitigate the issue.

More broadly, Rust's design philosophy is to take a holistic perspective: we try to avoid creating silos around std vs the language vs the compiler vs Cargo vs other tools vs the ecosystem, and instead focus on the quality of the end-to-end user experience for Rust. That's another reason that the approach to stability extends beyond the pure "language definition" sense.

I do want to say that, while in principle I'd like Rust to be well-defined enough that it's possible do a completely independent implementation of the language+libraries, I'm also really glad there is only one, and the focus has been on developer experience.

1 Like