The std::cell::Ref
struct in Rust is defined as follows:
pub struct Ref<'b, T: ?Sized + 'b> {
// NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a
// `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
// `NonNull` is also covariant over `T`, just like we would have with `&T`.
value: NonNull<T>,
borrow: BorrowRef<'b>,
}
The // NB
comment (I assume Nota bene / Nasty Bug or something?) implies that the following definition would not work, because it would be a noalias
violation (do they mean the LLVM attributes in the backend?):
pub struct Ref2<'b, T: ?Sized + 'b> {
value: &'b T,
borrow: BorrowRef<'b>,
}
I don't understand this point, as I was under the impression that the non lexical lifetime semantics were properly preserved in code generation. Otherwise the following simple example (which of course compiles) would also be illegal, right?:
struct Foo<'a> {
v: &'a i32,
}
fn foo(x: &mut i32) {
let f = Foo { v: x };
*x = 5; // value modified while the `noalias` f.v pointer is still in scope
}
Could somebody with more knowledge about the internals shed some light on this for me? I fear that I am misunderstanding something critical here, leading to potential issues in my own unsafe code.
Non lexical lifetimes were never a property of code generation. They were purely a borrow checker property, and borrow checking never impacts code generation.
The example you provided is not illegal according to LLVM. The problem with
noalias
only manifests with function parameter, because only they get anoalias
attribute (at least currently). So, the only way to write a problematic code without unsafe code will be:Except it doesn't compile, because you're moving a borrowed
v
. So there was actually no way to trigger a miscompilation.With
RefCell
however, we can do that:Which would be LLVM UB if
Ref
would use a reference.