Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign uprefcell_ref.md: RefCell::RefMut is fine #5
Comments
This comment has been minimized.
This comment has been minimized.
|
Not sure of it. It's the "disposing of example: fn foo(var: &mut u32) -> u32 {
let ptr: *mut u32 = var;
let ref_ = &mut *var;
mk_eqty!(ref_, var);
*ref_ = 2;
unsafe { *ptr }
}The |
This comment has been minimized.
This comment has been minimized.
What are you referring to here? I also don't see the relation between your example and
|
This comment has been minimized.
This comment has been minimized.
|
But fn foo(var: &mut u32) -> u32 {
let ptr: *mut u32 = var;
let ref_ = &mut *var;
mk_eqty!(ref_, var);
*ref_ = 2;
{ let _x = ref_; }
unsafe { *ptr }
} |
This comment has been minimized.
This comment has been minimized.
|
You can also have a write-sided version of this, which is more likely to be misoptimized (because abstract-interpretation optimizations move writes backward and reads forward). fn foo_will_break(var: &mut u32) -> u32 {
let ptr: *mut u32 = var;
let ref_ = &mut *var;
mk_eqty!(ref_, var);
let result = *ref_;
{ let _x = ref_; }
unsafe { *ptr = 2; }
result
}If the compiler DSEs the write to |
This comment has been minimized.
This comment has been minimized.
|
What I am saying in my first post is: The semantic type This is in contrast to impl<'b, T> Ref<'b, T> {
fn get_ptr(self) -> &'b T {
self.value
}
}The latter is the observation I brought up in a mail to Niko, which is the source of Unfortunately, I don't understand what this observation has to do with the code you are bringing up. There's no compiler transformations involved here, I am just looking at the flow of ownership. Here, "ownership" is a fairly wide term, it refers to anything you can "own" in Rust -- in particular, you can "own a mutable borrow of a |
This comment has been minimized.
This comment has been minimized.
|
The problem is that, after inlining, fn refcell_example(r: &mut RefCell<u32>) -> u32 {
let ref_ = r.borrow_mut();
let result = *ref_;
drop(ref_);
*r.borrow_mut() = 2;
return result;
}An My chain-based model allows you to "expose" a reference and remove its control over accesses, but |
This comment has been minimized.
This comment has been minimized.
|
I would argue there is a very fundamental difference between If we have a semantics that reasons by looking at types (e.g. to exclude aliasing), I am not surprised that doing such a type-erasing kind of inlining breaks programs. In other words, the unsafe code in |
This comment has been minimized.
This comment has been minimized.
|
But the example does not mess with types. It only messes with privacy. I am a little worried about introducing privacy to the aliasing rules. |
This comment has been minimized.
This comment has been minimized.
|
Privacy is the one mechanism in Rust which makes abstraction work. Without privacy, the entire story of "ascribing additional meaning to types" (and therefore the entire story of hiding unsafety behind an abstraction barrier) breaks down. |
This comment has been minimized.
This comment has been minimized.
|
Sure. But UB does not care about which additional meaning you ascribe to your types - if it did, then In your way, the aliasing rules would have to say something like "if you drop a struct containing an private |
nikomatsakis
added
T-libs
K-Code-Example
T-lang
and removed
T-libs
labels
Sep 2, 2016
This comment has been minimized.
This comment has been minimized.
|
@RalfJung I do not see the difference between The example I gave in the original Tootsie Pop model post seems to apply. Imagine that we had this function: impl<'b> RefMut<'b, u32> {
pub fn broken(self) {
let x = *self.value;
mem::drop(self.borrow);
use(x);
}
}It seems to me that, at least under an aggressive set of rules, the compiler would be within its rights to reorder those statements: impl<'b> RefMut<'b, u32> {
pub fn broken(self) {
mem::drop(self.borrow);
let x = *self.value;
use(x);
}
}After all, (And of course to make this all more deadly we can think of |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis You are right. I missed the fact that it's the destructor of However, if we instead consider an alternative where |
This comment has been minimized.
This comment has been minimized.
Hmm, interesting. I'm not sure if it is true that impl<'b> RefMut<'b, u32> {
pub fn broken(self) {
let x = *self.value;
mem::drop(self); // NB, not self.borrow
use(x);
}
} |
This comment has been minimized.
This comment has been minimized.
|
Now the version of the code which dereferences after the Consider fn foo(v: Vec<i32>) {
let x = v[0];
mem::drop(v);
use(x);
}Sure the compiler is not allowed to move the |
This comment has been minimized.
This comment has been minimized.
|
@RalfJung that seems (at least potentially) different to me; the |
This comment has been minimized.
This comment has been minimized.
mystor
commented
Sep 12, 2016
|
Consider this: struct Foo<'a>(&'a i32);
fn foo<'a>(f: Foo<'a>) {
use(f.0)
}
fn foo_prime<'a>(f: Foo<'a>) {
let x = f.0;
drop(f);
use(x);
}Basically, foo could be rewritten to foo_prime, which is okay for Foo, but not for RefMut. |
This comment has been minimized.
This comment has been minimized.
I disagree. @mystor |
This comment has been minimized.
This comment has been minimized.
|
@RalfJung I see your point. Very interesting! Have to think about it, but what you're saying makes sense to me. |
This comment has been minimized.
This comment has been minimized.
|
I just noticed though that this is not stable under inlining -- if the destructor is inlined in your Or maybe that's not a problem. After all, in the case of |
This comment has been minimized.
This comment has been minimized.
That's what I was talking about. |
This comment has been minimized.
This comment has been minimized.
|
(Beware of the zombies!) Now that we have done the proof, I am thinking maybe even Maybe we don't want to permit this kind of "irreversibility" when checking whether the private invariants are strong enough to justify the written type, I don't know. If we do not, then I would agree with you that |
This comment has been minimized.
This comment has been minimized.
|
Actually, based on @comex's comment about "Stacked Borrows", I now conclude that |
RalfJung commentedAug 17, 2016
•
edited
refcell_ref.mdsays:However, I think that applies only to
RefCell::Ref, not toRefCell::RefMut. (The mistake is probably mine in the original mail to Niko.) The reason for this is that&mut Tis non-Copy, so this actually is a reference that's valid for as long as the lifetime says. If the destructor ofRefCell::RefMutis executed, ownership is moved fromRefMutback to theRefCell. In some sense, having a variable of type&'a mut Tdoes not actually guarantee that the borrow lives for lifetime'a, all it really says is that if we keep hold of this variable, then the borrow lasts. But if we give up the variable, pass it to someone else, that someone may well kill the borrow earlier.In contrast to that,
&TisCopy, so there's no "giving up" of ownership here.(I was unsure whether an issue or the internals is the right place for this discussion. Feel free to move.)