-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Loosened rules involving statics mentioning other statics #51110
Conversation
@@ -228,38 +222,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { | |||
|
|||
/// Check if a Local with the current qualifications is promotable. | |||
fn can_promote(&self, qualif: Qualif) -> 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.
Can you inline can_promote
now?
@@ -492,7 +455,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | |||
match *place { | |||
Place::Local(ref local) => self.visit_local(local, context, location), | |||
Place::Static(ref global) => { | |||
self.add(Qualif::STATIC); | |||
if !(self.mode == Mode::Static || self.mode == Mode::StaticMut) { |
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.
Can you add a comment above this if
saying that only statics can refer to other statics?
@@ -677,16 +618,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | |||
Rvalue::Len(_) => { | |||
// Static places in consts would have errored already, | |||
// don't treat length checks as reads from statics. |
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 remove the comment here.
@@ -677,16 +618,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | |||
Rvalue::Len(_) => { | |||
// Static places in consts would have errored already, | |||
// don't treat length checks as reads from statics. | |||
self.qualif = self.qualif - Qualif::STATIC; | |||
} | |||
|
|||
Rvalue::Ref(_, kind, ref place) => { | |||
// Static places in consts would have errored already, | |||
// only keep track of references to them here. |
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 remove the comment here.
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.
Oops, yep. Should have paid more attention to the comments!
cc @oli-obk @nikomatsakis This PR allows const fn read<T: Copy>(r: &T) -> T { *r }
static FOO: u32 = read(&BAR);
static BAR: u32: 10; That is, the analysis being removed here was an unsound approximation. In terms of motivation, simplifying the analysis |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
} else { | ||
"cannot refer to statics by value, use a constant instead" | ||
}; | ||
struct_span_err!(self.tcx.sess, self.span, E0394, "{}", msg) |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -566,12 +518,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { | |||
|
|||
ProjectionElem::Field(..) | | |||
ProjectionElem::Index(_) => { | |||
if this.mode != Mode::Fn && | |||
this.qualif.intersects(Qualif::STATIC) { | |||
span_err!(this.tcx.sess, this.span, E0494, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
27ce051
to
7f0fe80
Compare
This comment has been minimized.
This comment has been minimized.
d06d230
to
9b37382
Compare
This comment has been minimized.
This comment has been minimized.
@@ -305,7 +263,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { | |||
}) if self.mir.local_kind(index) == LocalKind::Temp | |||
&& self.mir.local_decls[index].ty.is_box() | |||
&& self.local_qualif[index].map_or(false, |qualif| { | |||
qualif.intersects(Qualif::NOT_CONST) | |||
qualif.contains(Qualif::NOT_CONST) |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -416,7 +374,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { | |||
|
|||
// Account for errors in consts by using the | |||
// conservative type qualification instead. | |||
if self.qualif.intersects(Qualif::CONST_ERROR) { | |||
if self.qualif.contains(Qualif::CONST_ERROR) { |
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.
Okay I just checked, and using contains
is fine except for CONST_ERROR
because it's a union of several other flags, and any of the flags in that union
being set are relevant.
@@ -1026,7 +951,7 @@ This does not pose a problem by itself because they can't be accessed directly." | |||
|
|||
if let Some((ref dest, _)) = *destination { | |||
// Avoid propagating irrelevant callee/argument qualifications. | |||
if self.qualif.intersects(Qualif::CONST_ERROR) { | |||
if self.qualif.contains(Qualif::CONST_ERROR) { |
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.
Same here, needs intersects
.
This comment has been minimized.
This comment has been minimized.
9b37382
to
a40be59
Compare
This comment has been minimized.
This comment has been minimized.
4413c45
to
57c531f
Compare
This comment has been minimized.
This comment has been minimized.
eaf3096
to
8d7434a
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
8d7434a
to
c72a028
Compare
This comment has been minimized.
This comment has been minimized.
self.add(Qualif::NOT_CONST); | ||
return; | ||
} | ||
let static_kind = if self.tcx.is_foreign_item(global.def_id) { |
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.
s/static_kind
/runtime_static
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'm pretty sure static_kind
makes the most sense here... it's not a boolean, it's an Option<&'static str>
.
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 is irrelevant anyway now since we can't actually deny anything here.
@@ -29,5 +29,6 @@ extern "C" { | |||
} | |||
|
|||
static B: &'static c_int = unsafe { &test_static }; | |||
//~^ ERROR foreign statics cannot be accessed at compile-time |
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.
Wait, this is correct... I think we have to allow it, and instead make miri
disallow reads from them. Sorry about the detour, I didn't realize borrowing extern static
was already allowed.
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.
Okay, no worries.
☔ The latest upstream changes (presumably #51884) made this pull request unmergeable. Please resolve the merge conflicts. |
@eddyb What caused the latest Travis failures? Did something change in master when I rebased? (Because everything was passing before the clean-up, i.e. last commit.) I'm wary of simply hanging the expected error messages. |
@alexreg due to conflicts you need to rebase anyway. Rerun the test suite after you do, it looks like master changed indeed. I did recently change something around error reporting, so I think you can just adjust the error message comments to fit reality |
@oli-obk Okay, will do. |
Updated tests accordingly.
Updated tests accordingly.
@oli-obk Rebase didn't help. I fixed the errors now though. If you see the last commit, you'll notice the need to duplicate the error messages though. I wonder if this is a bug. |
c7a35eb
to
1c34227
Compare
@@ -13,6 +13,10 @@ | |||
extern { | |||
pub static symbol: (); | |||
} | |||
static CRASH: () = symbol; //~ cannot refer to other statics by value | |||
static CRASH: () = symbol; | |||
//~^ ERROR could not evaluate static initializer |
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.
open an issue about it. this feels like a regression by my diagnostics changes
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.
Okay. Or do you want to, since it's your changes? Either way.
@bors r=eddyb |
📌 Commit 1c34227 has been approved by |
Loosened rules involving statics mentioning other statics Before this PR, trying to mention a static in any way other than taking a reference to it caused a compile-time error. So, while ```rust static A: u32 = 42; static B: &u32 = &A; ``` compiles successfully, ```rust static A: u32 = 42; static B: u32 = A; // error ``` and ```rust static A: u32 = 42; static B: u32 = *&A; // error ``` are not possible to express in Rust. On the other hand, introducing an intermediate `const fn` can presently allow one to do just that: ```rust static A: u32 = 42; static B: u32 = foo(&A); // success! const fn foo(a: &u32) -> u32 { *a } ``` Preventing `const fn` from allowing to work around the ban on reading from statics would cripple `const fn` almost into uselessness. Additionally, the limitation for reading from statics comes from the old const evaluator(s) and is not shared by `miri`. This PR loosens the rules around use of statics to allow statics to evaluate other statics by value, allowing all of the above examples to compile and run successfully. Reads from extern (foreign) statics are however still disallowed by miri, because there is no compile-time value to be read. ```rust extern static A: u32; static B: u32 = A; // error ``` This opens up a new avenue of potential issues, as a static can now not just refer to other statics or read from other statics, but even contain references that point into itself. While it might seem like this could cause subtle bugs like allowing a static to be initialized by its own value, this is inherently impossible in miri. Reading from a static causes the `const_eval` query for that static to be invoked. Calling the `const_eval` query for a static while already inside the `const_eval` query of said static will cause cycle errors. It is not possible to accidentally create a bug in miri that would enable initializing a static with itself, because the memory of the static *does not exist* while being initialized. The memory is not uninitialized, it is not there. Thus any change that would accidentally allow reading from a not yet initialized static would cause ICEs. Tests have been modified according to the new rules, and new tests have been added for writing to `static mut`s within definitions of statics (which needs to fail), and incremental compilation with complex/interlinking static definitions. Note that incremental compilation did not need to be adjusted, because all of this was already possible before with workarounds (like intermediate `const fn`s) and the encoding/decoding already supports all the possible cases. r? @eddyb
☀️ Test successful - status-appveyor, status-travis |
…ification, r=oli-obk Removed `uninitialized_statics` field from `Memory` struct in miri **IMPORTANT: Do not merge until rust-lang#51110 is merged** r? @oli-obk CC @eddyb
Before this PR, trying to mention a static in any way other than taking a reference to it caused a compile-time error. So, while
compiles successfully,
and
are not possible to express in Rust. On the other hand, introducing an intermediate
const fn
can presently allow one to do just that:Preventing
const fn
from allowing to work around the ban on reading from statics would crippleconst fn
almost into uselessness.Additionally, the limitation for reading from statics comes from the old const evaluator(s) and is not shared by
miri
.This PR loosens the rules around use of statics to allow statics to evaluate other statics by value, allowing all of the above examples to compile and run successfully.
Reads from extern (foreign) statics are however still disallowed by miri, because there is no compile-time value to be read.
This opens up a new avenue of potential issues, as a static can now not just refer to other statics or read from other statics, but even contain references that point into itself.
While it might seem like this could cause subtle bugs like allowing a static to be initialized by its own value, this is inherently impossible in miri.
Reading from a static causes the
const_eval
query for that static to be invoked. Calling theconst_eval
query for a static while already inside theconst_eval
query of said static will cause cycle errors.It is not possible to accidentally create a bug in miri that would enable initializing a static with itself, because the memory of the static does not exist while being initialized.
The memory is not uninitialized, it is not there. Thus any change that would accidentally allow reading from a not yet initialized static would cause ICEs.
Tests have been modified according to the new rules, and new tests have been added for writing to
static mut
s within definitions of statics (which needs to fail), and incremental compilation with complex/interlinking static definitions.Note that incremental compilation did not need to be adjusted, because all of this was already possible before with workarounds (like intermediate
const fn
s) and the encoding/decoding already supports all the possible cases.r? @eddyb