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 upCoercing &mut to *const should not create a shared reference #56604
Comments
This comment has been minimized.
This comment has been minimized.
|
I believe the relevant code might be rust/src/librustc_typeck/check/coercion.rs Lines 734 to 752 in f504d3f but I am not sure because this is type checking. I have no idea where the code that decides about lowering of coercions to reborrows/casts lives. |
This was referenced Dec 7, 2018
This comment has been minimized.
This comment has been minimized.
What do you mean? That code is generating a deref and a borrow, each of those gets turned into the equivalent MIR later, and the result is as if the user wrote |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis I believe the comment above |
This comment has been minimized.
This comment has been minimized.
|
@eddyb that code generates "adjustments". I have no idea what that means. I found no use of |
This comment has been minimized.
This comment has been minimized.
|
Oh I got confused as to what this is doing, I missed the rust/src/librustc_mir/hair/cx/expr.rs Lines 152 to 180 in 1c3236a So looking again at the testcase, Changing EDIT: this may be backwards incompatible if the mutable reference being coerced was also already immutably borrowed, since you'd be introducing a mutable reborrow. Maybe we can avoid reborrows altogether here? But I'd leave that to @nikomatsakis. |
This comment has been minimized.
This comment has been minimized.
I did the first but not the last and tests seem to still pass...^^ But yeah, there is a compatibility problem with outstanding shared references. Ouch. |
This comment has been minimized.
This comment has been minimized.
|
Without |
This comment has been minimized.
This comment has been minimized.
Yeah I figured. run-pass and ui tests all pass, so either the change did nothing or nothing checks the MIR^^ For the backwards compatibility issue: |
This comment has been minimized.
This comment has been minimized.
|
I guess the tricky bit with making it a cast is that what we have to work with is Instead, we could just add to the MIR a sort of "borrow to raw pointer" (a lot like |
This comment has been minimized.
This comment has been minimized.
You mean like rust-lang/rfcs#2582? :D |
This comment has been minimized.
This comment has been minimized.
|
@RalfJung Yupp, that's what I mean, that seems like the perfect solution here. |
RalfJung
closed this
Dec 9, 2018
RalfJung
reopened this
Dec 9, 2018
This comment has been minimized.
This comment has been minimized.
|
(Sorry, that was the wrong button.) Notice that the lint discussed in rust-lang/rfcs#2582, at least when implemented on the MIR, would actually flag these reborrows that are in our way now: The new reference is created just to turn it into a raw pointer, it has no other use. |
Centril
added
the
T-lang
label
Dec 11, 2018
This comment has been minimized.
This comment has been minimized.
Following this idea, I have updated miri to treat the case of "escaping a ptr to be usable for raw accesses" as just another reborrow: Both trigger a retag; the retag after a cast-to-raw is a bit special because it also retags raw pointers. So, I am convinced now that the best way forward is to entirely ditch ref-to-raw casts, and encode them all as reborrow-to-raw. |
This comment has been minimized.
This comment has been minimized.
|
I found another case of this pattern in the wild, see https://internals.rust-lang.org/t/writing-through-a-pointer-derived-from-a-shared-reference-after-that-reference-is-dead/9078 |
This comment has been minimized.
This comment has been minimized.
|
This is actually more subtle than I thought. The thing that I did not know when discovering this problem is that the borrow checker treats let x = &mut 0;
let shared = &*x;
let y = x as *const i32; // if we use *mut here instead, this stops compiling
let _val = *shared;IOW, the borrow checker considers a cast to a This means that if we pull through with the plan to make So, everybody in rust-lang/unsafe-code-guidelines#106 (including me and @nikomatsakis but also in particular @SimonSapin) seemed to feel that the following code should be allowed (that's from the OP here): // example 1
let x = &mut 0;
let y: *const i32 = x;
unsafe { *(y as *mut i32) = 1; }But how do we all feel about this code? // example 2
let x = &mut 0;
let shared = &*x;
let y: *const i32 = x;
let _val = *shared;
unsafe { *(y as *mut i32) = 1; }Here, we cannot possibly maintain the idea that Does that mean that we are fine with example 2 being UB? And if it does, does that change our position about example 1? My opinionPersonally I am less sure now about example 1, and feel maybe it should actually be UB, after all. The reason for this is that it leads to the cleanest model, with fewer cases to consider. I will go into several possible models for a bit here. Clean and strictThe IMO most clean model (with the usual caveat that we'll not know how clean this ends up being until it got implemented and tested) is that the behavior of cast-to-raw depends only on the type of the raw pointer ( Basically, However, this would rule out the example in the OP, and run counter the idea (that I repeated a lot) that Extrapolate from current behaviorCurrently, we accept example 1 if it gets changed to
This would allow example 1. disallow example 2, and it would also disallow the following: // example 2
let x = &mut 0;
let shared = &*x;
let y: *const i32 = x;
unsafe { *(y as *mut i32) = 1; }
Two-phase@matthewjasper suggested it might be possible to treat cast-to- |
This comment has been minimized.
This comment has been minimized.
No, I'm was saying that a model that accepts both example 1 and 2 would effectively be modelling two-phase borrows. I can't see any way to allow this without complicating the model significantly. |
RalfJung commentedDec 7, 2018
•
edited
It has long been a rule in Rust that you must not mutate through a shared reference, or a raw pointer obtained from a shared reference.
Unfortunately, that rule currently forbids the following code:
The reason for this is that coercing
&mut Tto*const Timplicitly first creates a shared reference and then coerces that to*const T, meaningyin the example above is technically a raw pointer obtained from a shared reference.We should fix our coercion logic to no longer create this intermediate shared reference.
See #56161 for how we uncovered this problem.
Cc @eddyb @nikomatsakis