-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Ignore borrowck for static lvalues and allow assignment to static muts #46032
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
54d3a29
to
64b7ac5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Just a few nits.
src/librustc_mir/borrow_check.rs
Outdated
context, common_prefix, lvalue_span, bk, | ||
&borrow, end_issued_loan_span) | ||
// Ignore all borrows rooted in statics | ||
if let Lvalue::Local(_) = *self.prefixes(lvalue_span.0, PrefixSet::All).last().unwrap() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this if let
makes me nervous -- if we add more lvalues in the future, it might silently squash them. I'd rather see an exhaustive match
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, something like:
if let Lvalue::Static(_) = ... {
// Skip checks on statics. See #123.
} else {
...
}
src/librustc_mir/borrow_check.rs
Outdated
&borrow, end_issued_loan_span) | ||
// Ignore all borrows rooted in statics | ||
if let Lvalue::Local(_) = *self.prefixes(lvalue_span.0, PrefixSet::All).last().unwrap() { | ||
self.each_borrow_involving_path( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, I think this should be factored into a helper function personally
e4190a5
to
0c62bfd
Compare
Done. r? @nikomatsakis |
// revisions: ast mir | ||
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir | ||
|
||
// Test file taken from issue 45129 (https://github.com/rust-lang/rust/issues/45129) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is UB. Please don't have it as a run-pass test, because verified compilation will correctly complain.
to make it non-UB, e.g. use an array and access different array indices:
struct Foo { x: [usize; 2] }
static mut SFOO: Foo = Foo { x: [23, 32] };
impl Foo {
fn x(&mut self) -> &mut usize { &mut self.x[0] }
}
fn main() {
unsafe {
let x = SFOO.x();
SFOO.x[1] += 1;
*x += 1;
}
}
src/librustc_mir/borrow_check.rs
Outdated
// Check permissions | ||
self.check_access_permissions(lvalue_span, rw); | ||
|
||
match *self.prefixes(lvalue_span.0, PrefixSet::All).last().unwrap() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't borrows involving statics be handled in the same way as borrows involving raw pointers? I could open another issue for borrows through raw pointers, but they should be handled similarly.
struct Foo { x: [usize; 2] }
static mut SFOO: Foo = Foo { x: [23, 32] };
impl Foo {
fn x(&mut self) -> &mut usize { &mut self.x[0] }
}
fn main() {
// this compiles with AST borrowck, should also compile with MIR borrowck
unsafe {
let sfoo: *mut Foo = &mut SFOO;
let x = (*sfoo).x();
(*sfoo).x[1] += 1;
*x += 1;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree they should be handled the same. Do you think that means however that we should unify the codepaths that make those two cases behave the same? If so, how do you think we should do it? (I think that sounds like a nice idea)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(In particular, if we decided to offer a lint at some point for obvious UB, I'd want it to apply to both.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess if we just factor out a helper function unsafe_access_path(&Lvalue)
it can test for either a deref of a raw pointer or the use of a static mut
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One I think good way of doing it is just not registering borrows that go through an unsafe access path.
Note that for unsafe pointers this is different from checking the lvalue, but it is what AST borrowck does, e.g. in:
fn main() {
let mut x = 1 as *const ();
let y = unsafe { &*x }; // borrow through an unsafe pointer
let _ = &mut x; // then mutate everything - should be OK
println!("{:p}", y);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was avoiding recommending that because I wanted to consider adding a lint later on. But we could do it that way. Either way, we basically need the helper function I talked about, I imagine. It's just a question of whether we try to avoid registering the borrows in the first place, or try to ignore them when they are registered. =)
btw,
|
0c62bfd
to
086b92c
Compare
Alright, I've made changes to the test file, and am now also not registering borrows that are from an unsafe lvalue. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! I just think one part of it is no longer needed.
|
||
match *lvalue { | ||
Local(_) => false, | ||
Static(_) => true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only static mut
is an unsafe lvalue, not any static at all.
src/librustc_mir/borrow_check.rs
Outdated
context, common_prefix, lvalue_span, bk, | ||
&borrow, end_issued_loan_span) | ||
} | ||
fn check_borrows_readability_or_writability<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are suppressing the borrows, we shouldn't need any changes to this function.
let sfoo: *mut Foo = &mut SFOO; | ||
let x = (*sfoo).x(); | ||
(*sfoo).x[1] += 1; | ||
*x += 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! =)
f22266b
to
374047d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lookin' good. One nit.
@@ -164,6 +203,34 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { | |||
} | |||
} | |||
} | |||
|
|||
fn is_unsafe_lvalue(&self, lvalue: &mir::Lvalue<'tcx>) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have this same function twice? Copy-and-paste error?
374047d
to
c9d1db7
Compare
e11a29a
to
f8ba371
Compare
@bors r+ |
📌 Commit f8ba371 has been approved by |
☀️ Test successful - status-appveyor, status-travis |
Fixes #45129.
Fixes #45641.