-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Treat extern statics just like statics in the "const pointer to static" representation #67630
Conversation
…c" representation
I don't understand what this does, but intuitively it makes sense to me that extern statics would be more like |
And this is where I learn that there is such a thing as rust/src/librustc_typeck/collect.rs Lines 2396 to 2406 in a916ac2
We now treat |
So |
I... have no clue. Neither the nomicon (https://doc.rust-lang.org/nomicon/ffi.html?highlight=static#accessing-foreign-globals) nor the reference (https://doc.rust-lang.org/nightly/reference/items/external-blocks.html?highlight=extern#external-blocks) helped me clear this up. What I think should be true: It's up to the user to guarantee this. If C code modifies an What I know is true: It's unsafe to read from |
The issue fixed by this PR shows that rustc thought it is legal to create an immutable reference to an extern static just like it is legal to do so to a normal static. Inside the initializer of a static, these even got promoted (which is what this PR fixes so it happens again). |
Alternatively we can teach promotion to promote |
It seems reasonable for rustc to (unsafely) permit creating shared references to Cc @rust-lang/lang |
I agree with @oli-obk's summary. I do think we should clearly write this up -- probably in the reference and nominicon too. |
I'll adjust the reference and nomicon to clear this up |
We looked at this in the @rust-lang/lang team meeting but we were feeling a bit confused, actually -- @oli-obk maybe you can step back and give just a bit more context about this PR? e.g., what is the behavior now that you are changing? |
This is a bugfix for a wrong assumption I made when I wrote that code. I assumed that extern statics are inherently unsafe to access, not just because of type punning, but also because the underlying static may change its value even though it's an immutable static from the rust perspective. The C code defining the extern static may change the static and the rust code would have to be able to cope with that. Raw pointers allow this, because they require the user to uphold the aliasing invariants themselves. So I chose to make any access to an extern static (so any user mention of |
Taking the example from the issue: extern "C" {
static X: i32;
}
static mut FOO: *const &'static i32 = [unsafe { &X }].as_ptr(); is essentially extern "C" {
static X: i32;
}
static mut FOO: *const &'static i32 = {
const CONST_PTR_TO_X: *const i32 = &X;
let _tmp0: *const i32 = CONST_PTR_TO_X;
let _tmp1: &'static i32 = unsafe { &*X };
let _tmp2: [&'static i32; 1] = [_tmp1];
let _tmp3: &[&'static i32; 1] = &_tmp2;
let _tmp4: *const &'static i32 = _tmp3.as_ptr();
_return_location = _tmp4;
StorageDead(_tmp4);
StorageDead(_tmp3);
StorageDead(_tmp2); // this is where the pointer becomes dangling
StorageDead(_tmp1);
}; Before #66587 the code was extern "C" {
static X: i32;
}
static mut FOO: *const &'static i32 = {
let _tmp1: &'static i32 = unsafe { &X };
let _tmp2: [&'static i32; 1] = [_tmp1];
let _tmp3: &[&'static i32; 1] = &_tmp2;
let _tmp4: *const &'static i32 = _tmp3.as_ptr();
_return_location = _tmp4;
StorageDead(_tmp4);
StorageDead(_tmp3);
// by not emitting a StorageDead here, we leak the value
// to the const evaluator, which will intern all memory
// reachable from the `_return_location` value and then drop
// all leftover memory.
StorageDead(_tmp1);
}; The reason we started emitting a const CONST_PTR_TO_X: *const i32 = &X;
let _tmp0: *const i32 = CONST_PTR_TO_X;
let _tmp1: &'static i32 = unsafe { &*X }; and dereferencing raw pointers will stop promotion from happening. I believe that my decision in #66587 to use raw pointers was wrong to start out with. |
@oli-obk I see, so you see this as regarding the status quo, in other words. I'm inclined to agree. I do think we should document this requirement. The current reference https://doc.rust-lang.org/nightly/reference/items/external-blocks.html doesn't really say much one way or the other. I do remember that we added the code to make them unsafe to access relatively late, which implies that (initially) we thought of them exactly as statics. Might be interesting to find the PR that did that, but it'd require some significant archaeology. If I understand, essentially we want to say the following: An "extern static" declares a static variable found in external code, typically C. Note that extern statics must meet the same requirements as ordinary Rust statics, and thus that
Does that sound correct? Each of that statements would ideally link to a section that explains what it means, but for now perhaps we can link to issues on the unsafe code guidelines repository. |
Initializing them is a kind of use, though, isn't it?^^ |
Yes, better. This is what I meant. |
Well, actually, there was this question that I kind of ignored but did ponder in the back of my head. Must they be initialized before you invoke Rust code -- or just before Rust code tries to access them...? Probably safe enough to say "before invoking Rust code that could access them" or something like that. |
Yeah, good question. I don't know the answer. That's why I picked the conservative option. |
Would we need to be careful around these extern statics, and for example not apply I believe the conservative option indicates that code like the following is not fine, and as such we are permitted (for example) to move the access of the static up above the C call. That might not be very intuitive, but would be fine I think (since the access is unsafe). Maybe we can call it out explicitly in documentation, though. However, AFAICT, the proposed conservative definition essentially makes any Rust library that uses such an extern static generally unusable -- it must declare to the world that it cannot be packaged or otherwise used "inside C" or "as if it was C code" -- right? That feels not great, but maybe there's not much we can do without relaxing our proposed definition too much. extern {
static SOME_STATIC: &u32;
}
fn foo() -> Option<u32> {
if call_c_init() {
// okay, C initialized it
*SOME_STATIC
} else { None }
} |
@Mark-Simulacrum Why would one not use I don't know how |
That is essentially what I thought first, and why we are in this situation. I made uses of extern statics be a raw pointer deref (not "just like", but really, the MIR of a raw pointer deref and of an extern static access were 100% equivalent before this PR). But considering the fact that there's lots of user code out there that creates
Yes, see above. We can discuss whether to break this, whether to start start linting it or whether it's correct, but I think we should merge this PR sooner than later because otherwise we turn a beta regression into a stable regression just because I made an uninformed decision in a previous PR. |
|
@oli-obk I guess the distinction is that extern statics can't be dereferenced "spuriously" (i.e. by the compiler) whereas normal statics can be, right? The user can of course go to a reference if they want. I think I'm mostly in favor of otherwise treating them purely as statics. I think going with "they're literally just statics" might also make sense (per what @RalfJung notes - if you need special behavior, use static mut). |
Well, since even writing The only reason extern statics are unsafe (as far as I can tell from historic PRs and issues) is that they could be used to transmute between types. I was unable to find a discussion saying (or even hinting at) that immutable extern statics can in fact be mutable and should thus be treated as a fancy way to write |
I guess it depends on if we want to go with Niko's proposal of "must be initialized" exactly like normal statics. I myself am fine with that proposal, I guess, though like @RalfJung I think I'm not entirely certain exactly what that means (i.e. any rust code or not is the question). But maybe it's not an important distinction - i.e. a definition that they must be essentially in rodata in the executable is fine. |
I don't think that is decided yet. We permit spurious reads from |
Well I can also implement a conservative alternative keeping the pointer representation but expanding promotion to allow borrows of extern statics |
No, I think making |
ok XD then I declare the discussion as resolved and will open the reference and nomicon PRs @bors r=spastorino |
📌 Commit 097e14d has been approved by |
…torino Treat extern statics just like statics in the "const pointer to static" representation fixes rust-lang#67612 r? @spastorino cc @RalfJung this does not affect runtime promotion at all. This is just about promotion within static item bodies.
Rollup of 12 pull requests Successful merges: - #67630 (Treat extern statics just like statics in the "const pointer to static" representation) - #67747 (Explain that associated types and consts can't be accessed directly on the trait's path) - #67884 (Fix incremental builds of core by allowing unused attribute.) - #67966 (Use matches macro in libcore and libstd) - #67979 (Move `intravisit` => `rustc_hir` + misc cleanup) - #67986 (Display more informative ICE) - #67990 (slice patterns: harden match-based borrowck tests) - #68005 (Improve E0184 explanation) - #68009 (Spell check librustc_error_codes) - #68023 (Fix issue #68008) - #68024 (Remove `-Z continue-parse-after-error`) - #68026 (Small improvements in lexical_region_resolve) Failed merges: r? @ghost
discussed in T-compiler meeting. beta-approved. Leaving nomination label for T-lang. |
[Beta] Backports I did not include #67134 and #67289 since they seem to be on beta already. * Fix up Command Debug output when arg0 is specified. #67219 * Do not ICE on unnamed future #67289 * Don't suppress move errors for union fields #67314 * Reenable static linking of libstdc++ on windows-gnu #67410 * Use the correct type for static qualifs #67621 * Treat extern statics just like statics in the "const pointer to static" representation #67630 * Do not ICE on lifetime error involving closures #67687
fixes #67612
r? @spastorino
cc @RalfJung this does not affect runtime promotion at all. This is just about promotion within static item bodies.