I don't really care about stacked borrows rn. I need miri for another bug, but it gets caught on stacked borrows before it reaches the code I actually need to test. Can I still disable stacked borrows in miri?
This contains the MIRIFLAGS you can use. Most notably -Zmiri-disable-stacked-borrows; but I've never used Miri before, so perhaps I don't understand the question.
I don't think so, but you can try a different sanitizer such as valgrind or address sanitizer.
Doesn’t that mean your program has undefined behavior? I don’t think any other bug matters while the compiler could do anything to the parts of the code encountering UB. If you’re genuinely on the edge case where stacked borrows is slightly too strict, then you can pass a flag to use tree borrows instead, I guess. But that case doesn’t seem likely to me.
that's not really helpful
You know far more than me about this, so can you explain why using the MIRIFLAGS -Zmiri-disable-stacked-borrows isn't correct? As my response admits, I've never used Miri before—since I write little unsafe code—but the README.md accessible on GitHub does list that as a flag one can use.
You can try adding -Zmiri-tree-borrows to the MIRIFLAGS environment variable. The downside is that tree borrows does not currently allow integer-to-pointer casts. If that’s necessary, I guess you’d need to use -Zmiri-disable-stacked-borrows, which, unfortunately, will not check all undefined behavior.
To be clear, you are probably violating Rust’s aliasing rules if you violate the Stacked Borrows aliasing rules. That is not good.
What does your code do to trigger the stacked borrow failure in Miri? A self-referential struct or something? I’m thinking of a time I used Box<T> for a self-referential struct and IIRC violated stacked borrows (because, as it turns out, using Box for that purpose is currently unsound).
#[derive(Debug)]
pub struct Syms<X>(UnsafeCell<Vec<X>>);
impl<X> Syms<X> {
pub fn new() -> Self {
Self(Vec::new().into())
}
#[inline]
fn buf(&self) -> *mut Vec<X> {
self.0.get()
}
}
impl<X> Syms<X>
where
X: PartialEq,
{
/** add a string to the symbol list. return a pointer to the str or
* the str in the list if it exists. */
/* hahaha. fuck you. */
#[inline]
pub fn add(&mut self, x: X) -> &mut X {
let buf = self.buf();
let L = unsafe { &mut *buf }.len();
for i in 0..L {
unsafe {
if &(&*buf)[i] == &x {
return &mut (&mut *buf)[i];
}
}
}
/* no matching string found, so we push it and return the ptr */
self.add_noeq(x)
}
}
impl<X> Syms<X> {
/** same as Self::add() but doesn't require PartialEq ie allows copies */
#[inline]
pub fn add_noeq(&mut self, x: X) -> &mut X {
let buf = self.buf();
let v = unsafe { &mut *buf };
let L = v.len();
v.push(x);
&mut v[L]
}
}
These lines:
unsafe {
if &(&*buf)[i] == &x {
return &mut (&mut *buf)[i];
}
}
are unsound. The borrow of (&*buf)[i] does not end before the body of the if block, so the second &mut borrow is not unique, and thus violates Rust’s aliasing rules. It would be possible to work around this with let condition = { &(&*buf)[i] == &x } or something, but that doesn’t seem like the best solution.
At first, I thought you might want to try using UnsafeCell::get_mut instead of get. Since the important methods take &mut self in the code you showed, the UnsafeCell shouldn’t be necessary at all, even.
But immediately following that reasoning, maybe you ran into a Polonius-style borrow check error; a conditional early return of an &mut X probably led to an overly-strict borrow check error. Am I right about that guess - did the borrow checker stop you from using the buffer because of the early return? Or was there a different reason you’re using UnsafeCell?
In maybe like an hour or so from now, I could show you how to rewrite that example code with only a single line of (sound) unsafe code. Moreover, in the nightly version of Rust, no unsafe would be needed at all.
honestly im just a c++ programmer at heart and i wrote this quickly. im pretty sure it works because it's been working. but im working on implementing something else which has unsafe which i need to target more, so i just wanted to skip over these warnings. if you can show me how to do this without unsafe at the same speed, then id welcome it
This is what I would write, without UnsafeCell: Rust Playground
The lifetime extension only occurs just before the early return, and thus the NLL borrow checker’s complaint about a second mutable borrow occurring later in the function is irrelevant; the code is sound, and it compiles under the Polonius borrow checker.
I would have the code be tested in CI on stable with the crate’s polonius feature disabled, and tested on nightly with -Zpolonius=next added to RUSTFLAGS and the crate’s “polonius” feature enabled.
Orrrrr just forget about the testing part and act as though polonius is never enabled, and remove the feature flag and #[cfg(..)].
If the UnsafeCell were needed for something, the code for these functions would merely need to change self.0 to self.0.get_mut().