Description
Currently, under both Stacked Borrows and Treed Borrows, the following code has defined behaviour:
use std::cell::Cell;
pub fn evil(x: &Result<bool, Cell<u8>>) -> bool {
unsafe { *(x as *const _ as *const bool as *mut bool).add(1) = false }
false
}
pub fn main() {
let x = Ok(true);
match &x {
Ok(true) if evil(&x) => unreachable!(),
Ok(false) => println!("Ok(false) = {x:?}"),
Ok(true) => println!("Ok(true) = {x:?}"),
Err(_) => unreachable!(),
}
}
(Credit to @digama0)
On current rustc, this compiles and prints Ok(true) = Ok(false)
. However, given no undefined behaviour, this seems to conflict with the fact that patterns are evaluated linearily -> Ok(false)
should hit first.
The question is: Does this code have UB (most likely in evil
) or is this defined behaviour. If we answer the latter, we must then answer whether rustc's lowering of the match is correct, or if a linear evaluation is correct.
From https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/How.20does.20match.20interact.20with.20.60.26UnsafeCell.60, it's clear to me that rustc's lowering should be deemed correct in some manner, but I also believe that we currently promise sequential evaluation (additionally, even if we don't, I believe that a naive lowering of cascading if
/else if
statements should be valid). Given the two constraints being conflicting, if both are satisfied, then the code must have undefined behaviour.