Maybe rust is too stable?

I was just reading through this thread about the never type: https://github.com/rust-lang/rust/pull/65355#issuecomment-542512584

One question is how to prevent this problem in the future (i.e. people relying on implementation choices that are not part of the language spec). An idea I had was to make compilation of unspecified behavior non-deterministic. That is relying on unspecified behavior would result in intentionally flaky compilation. For example, in any place where the compiler makes an implementation choice, it randomly chooses between two different implantations.

The implementations don't have to be that different, and this will not affect correct programs.

3 Likes

Could you give an example?

Well for the never type fallback, perhaps we could fall back to one of two different types, both of which have the same behavior, but that don't coerce to each other.

Another example would be changing around the bits in the rust ABI so that people can't rely on it.

I think it's possible to run a special crater run and collect these situations into the cargo-audit database... That's easier, and more helpful i think.

1 Like

Just to make sure I understand 100%... you mean that the compile will sometimes fail, not that it sometimes produces flaky code, and sometimes doesn't, right? Wouldn't this be better as a lint of some kind? Or some kind of panic statement?

If my compiler was doing something flaky, I'd quit it and move to a different compiler. Since the only compiler that is really available right now is rustc, that would effectively mean moving to a different language.

3 Likes

If the compiler can make a random decision, then it could also just print a warning. Randomly choosing between implementations seems like the kind of thing that will result in people shipping heisenbugs to their customers. It would be better to have the implementation determined by a compile flag (or some deterministic mechanism) and let people set the flag randomly if that's the behavior they want.

5 Likes

Well, no we can't print a warning in general because it is (I think) undecidable whether the code depends on the unspecified behavior.

1 Like

people relying on implementation choices that are not part of the language spec

This is sadly a recurrent theme. Not knowing Rust rules is not an excuse for writing unsafe code that is not safe, but it is unreasonable to expect Rust users to know all the rules required to write safe unsafe code, particularly when such rules are not written anywhere.

We do promise users that we don't let them write such code in safe Rust, but we are very clear that it is their responsibility to make sure that their unsafe Rust is correct, and that requires being proactive about it.

(In that particular issue, the users have fixed things, and backported fixes to older crates, so this approach can work)

An idea I had was to make compilation of unspecified behavior non-deterministic.

That would require that the people working on the compiler are aware of when they are actually modifying or introducing unspecified behavior, then have the time to go and make that behavior random, and be willing to give users worse performance, or debug information, or many other things to prevent them from relying on unspecified behavior.

2 Likes

Yes that's true. I also agree with the previous posts that instability is not generally a good thing. I was just tossing the idea out there!

So it seems the takeaways are as follows:

  1. If we do something like this, it should be opt-in (e.g. a flag or a separate tool)
  2. Such a tool would be actually useful, but would require some extra effort on the behalf of compiler devs. This suggests that unless there is a generic/automatable way of doing it, it likely won't happen because people are busy...
1 Like

Randomizing unspecified behavior doesn't necessarily requires nondeterministic build. Say, we can seed the RNG with the hash of the whole source code. Now we have fully deterministic build(at least not worse than now), but if we added some whitespaces on the code, struct fields are reordered and transmute::<&dyn Trait, (*mut u8, *mut u8)>() tests start failing.

2 Likes

I think a lot of stuff got conflated here.

For years, Rust has had far more accepted RFCs than it's ever had the time and resources to fully implement and stabilize (here's the earliest explicit discussion I know of, and here's a highly relevant and more recent Niko blog post, though I'm sure there are several other things I could link). IMO, the awkward situation with the never type is merely a poster child for this broader problem, since the never type was never a very high priority but turned out to require more effort to fully stabilize than anyone expected. If so, the solution here is to keep focusing efforts on finishing up what we've already started and continue postponing most new RFCs until we actually have the resources to commit to doing them properly.

This thread has so far mostly focused on a) the obvious downsides of intentionally randomizing compiler behavior, and b) the unsurprising impossibility of detecting when we're being "overly stable" and letting code rely on unstable or unspecified things. I completely agree with the conclusions that

But I'm surprised that nobody mentioned that a very similar feature called "optimization fuel" has been in rustc for over two years, because it was seen as a way to help unblock struct field reordering.

I believe this existing feature precisely covers the small subset of cases where "deliberate build randomization" actually makes sense, i.e. where all of the following are true:

  1. not only is it "opt in", but it can be completely disabled or explicitly set to any fixed value to not only make things predictable, but pin down exactly which bit of unpredictability triggered an issue
  2. this doesn't involve any unreproducible builds or genuine nondeterminism, only a deterministic optimization with no stability guarantee
  3. it affects optimizations that we absolutely want rustc to do (i.e. it's not just destabilization for destabilization's sake), yet have to be done on a best effort basis because achieving "perfect layout" in all corner cases is apparently not worth it
  4. struct layout is something people shouldn't rely on in general, but it's very plausible for unsafe code to end up accidentally relying on it in ways the compiler can't hope to detect

On the other hand, maybe 4 will become less true over time as miri improves, we finally get some unsafe code guidelines, and &raw becomes a thing.

4 Likes