Description
I think we should clarify in the definition of undefined behavior that:
-
While this document (and the reference) often call out the behavior that is undefined, the behavior that is not explicitly defined in these documents is also undefined.
-
Undefined behavior cannot be assumed to remain undefined.
To expand on this second point, we currently say that creating a &mut T
with value 0
is undefined behavior, but we guarantee that this will always be undefined behavior (because the type has a niche), and therefore, this code is sound:
fn foo(x: &mut T) { unsafe {
if transmute(x) == 0_usize {
*std::ptr::null() // never invokes UB
}
}}
Now follow me in this thought experiment, and imagine that, instead of guaranteeing that &mut T
are never 0
, we were just to say that creating a &mut T
with value 0
is UB.
Would the example above be sound? I don't think so, because we could, in a future release of Rust, make this undefined behavior defined in such a way that would make that code unsound. For example, we could say that creating a &mut T
with value 0
is ok, but dereferencing it panics, and that change in the language from undefined to defined behavior would break the code above.
That is, while the compiler can assume that undefined behavior never happens when optimizing Rust code, users cannot assume that undefined behavior will remain undefined when writing Rust code. The only thing users can assume are things that we guarantee.
If we want to say that some behavior is undefined, and that behavior will remain undefined forever, such that users can assume that it never happens, we need to word that as a guarantee, and breaking that guarantee would be a backward incompatible change.
So maybe we need to split our lists of undefined behavior into behaviors that are just undefined, and behaviors that are guaranteed to be undefined.