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
Unsafe Extern Blocks #3484
base: master
Are you sure you want to change the base?
Unsafe Extern Blocks #3484
Conversation
Co-authored-by: Jacob Lifshay <programmerjake@gmail.com>
Perhaps non- This would minimize churn in the language definition if any such targets appear in the future. (But maybe that's an insignificant concern since support for previous editions is still required.) |
a future RFC can always relax required keywords in certain situations, it wouldn't even be an edition-break issue at that point. but let's save that for a future RFC when we actually have a clear and specific fully working case. |
One concern about making the functions inside the For example, I could imagine people updating their projects, solving issues one by one, and resolving this issue by just changing an (Keep in mind that not every project uses |
maybe have a weak keyword unsafe extern "C" {
safe fn sqrtf(v: f32) -> f32;
unsafe fn printf(s: *const c_char, ...);
} |
I'm frustrated by this RFC because the overall direction is obviously right to me, but the overloading of |
One specific concern I have:
i.e., we have to weigh the risk of people accidentally writing |
Hey I'm all for a |
The T-lang team met to discuss this RFC today. We bikeshed'ed a bit and probably covered the same ground that was already covered in this comment thread above (e.g. the suggestion above of a weak In the end, we have the following proposal for how you might adjust this RFC to meet some broader goals that we identified. Proposal:(In this text, let the term "unadorned
Here are the various motivations/assumptions that led us to the proposal above:
|
@rustbot labels +I-lang-nominated +A-edition-2024 We met back on 2023-10-11 to discuss this RFC, and in that meeting, we hammered out a consensus for how to move forward on this as articulated by pnkfelix above. We've just been waiting for the RFC to be updated according to that consensus, and it now has been ( Let's nominate to discuss potentially moving forward on this for Rust 2024. |
Updates here look good! I this this is a good path forward and this version addresses the previous worries about changing something to be the opposite over an edition boundary. @rfcbot merge |
Team member @scottmcm has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. |
This is explained in the newly added sections of the RFC. The safety obligation can be split into two parts:
In The status quo is that obligation (1) is also attached to the call sites of the function. This doesn't make a ton of sense, since the correctness of the signature should be checked at the place where the signature is declared. Furthermore, unfortunately the signature has an effect on all calls of this function everywhere: if you make an argument |
@traviscross Thanks for updating the text! I think it now details all the alternatives proposed before the update. One possible implementation (pointed out by @kennytm above) that plays nicely with extern types and addresses the LLVM bug) would be “only require I think I remembered some prior art: cxx.rs does Their rules are:
Unfortunately, LLVM bug prevents these rules from being adopted, despited them being IMHO somewhat neater.
Sorry, now I’m confused. I think per RFC text, unsafe extern "C" {
// The obligation isn't discharged here.
unsafe fn strlen(str: *const c_char) -> usize;
}
// It's discharged here, where we know the inputs:
unsafe { strlen(1 as _) } // and we still can do it incorrectly Anyway, RFC doesn’t actually require the inner unsafe extern "C" {
/* no obligation discarded here */ fn strlen(str: *const c_char) -> usize;
} is also possible. |
Yes, that's what I said. The In contrast, the |
It's my understanding that this is, at least primarily, a rustc bug, not an LLVM bug. rustc is responsible for how it wants to handle attributes when there are multiple declarations/definitions of the same symbol. IIRC clang does handle this correctly in relevant cases by e.g. not emitting attributes like noalias on declarations. (Within one module, LLVM has no concept of multiple declarations/definitions for one symbol, this is fully a frontend concern. LLVM's own interpretation would only come into play during module linking.)
Only adding attributes to calls and not declarations is perfectly fine as far as LLVM is concerned, I don't see any implementation problems from that side. The only possible downside that comes to mind is that if a call gets devirtualized, we might have attributes with more information on the declaration rather than the (originally virtual) call. I doubt this is a significant problem though. (I'm pretty sure that we already discussed this in some details somewhere, but I can't find it.) |
Yeah, same here. Thanks for repeating your answer! That seems like there is a path forward for all our backends that avoids rust-lang/rust#46188: do not add attributes to declarations ever, only at call sites. My inclination would be that we should try that before considering #46188 motivation for a language change, i.e. the RFC should be updated to stand on its own feet, rather than relying on #46188. It seems like many people think that the separation of the |
Even if the C standard allows for what LLVM is doing, we could still conceivably fix LLVM. In the text, let's draw this out a bit more finely. (Thanks to RalfJ for raising this point.)
When people migrate to the new edition, if they have turned up the severity of the `unsafe_code` lint and they have `extern` blocks that need to be marked `unsafe`, they will see this lint as intended. Let's make a note of that. (Thanks to kennytm for raising this point.)
Thanks for the explanation. If Rust had "checked safety" it would be very worthwhile to distinguish between "unsafety by matching function signature" vs "unsafety of the function itself". I'd argue that the forward-declared items themselves should already be taught as being unsafe in that you have to match the function signature with the actual library. Otherwise simply forcing the So I'm still -0 on the double unsafe since it's still cringe to see that when taking the lang design theorist glasses off.
|
This RFC had used as one motivation that undefined behavior can currently result simply from having an incorrect `extern` block even if the items within are not used from Rust code. This motivation was not cited in the lang team's 2023-10-11 consensus for how and why to proceed with this RFC, and it's possible that we may be able to resolve this issue in other ways, so let's remove that motivation from this RFC. (Thanks to RalfJ for raising this point and suggesting this.)
The 2023-10-11 consensus from lang on how to move forward included that the `safe` and `unsafe` keywords would be optional within an `unsafe extern` block. The reference-level section correctly followed this consensus, but the guide-level section did not, and suggested that annotating items with these keywords was required. Let's fix that.
That's exactly what this RFC does: making the declaration unsafe. It's the The |
On this thread, there had been a lot of confusion by reasonable people about whether items within an I think I understand that now. The reference-level section correctly followed the 2023-10-11 lang team consensus on this point (that these are optional), but the guide-level section had language that suggested that these were required. Somehow we seem to have missed this contradiction. I've fixed this now (these annotations are optional, and if not present, an item is assumed to be |
As @RalfJung suggested, I've now removed the rust-lang/rust#46188 issue as a motivation (and have added a section noting this history). |
@rustbot labels +I-lang-nominated While it's a bit unusual, let's nominate to confirm:
|
// in the bureaucratic ideal world
extern {
trusted(extern_signature) unsafe(deref_pointer) fn fopen(pathname: *const c_str, mode: *const c_str) -> *mut FILE;
trusted(extern_signature) safe fn malloc(size: usize) -> *mut c_void;
}
// but because you can't provide the reason directly (and it's annoying anyway) it becomes
extern {
trusted unsafe fn fopen(pathname: *const c_str, mode: *const c_str);
trusted safe fn malloc(size: usize) -> *mut c_void;
}
// but because we conflate trusted and unsafe it becomes
extern {
unsafe unsafe fn fopen(pathname: *const c_str, mode: *const c_str);
unsafe safe fn malloc(size: usize) -> *mut c_void;
}
// but because `unsafe unsafe` and `unsafe safe` is even sillier it becomes
unsafe extern {
unsafe fn fopen(pathname: *const c_str, mode: *const c_str);
safe fn malloc(size: usize) -> *mut c_void;
} what I'm saying is that the extern { // implies `trusted(extern_signature)` and will emit `unsafe_code` lint if it contains any `fn`/`static` item
unsafe fn fopen(pathname: *const c_str, mode: *const c_str);
safe fn malloc(size: usize) -> *mut c_void;
} |
There is prior art to adding Having a single keyword to look for makes it easier for reviewers to know when they need to have something more thoroughly reviewed. On a multi-maintainer project that does not |
I don't agree with that framing. trusted(extern_signature) extern {
unsafe(deref_pointer) fn fopen(pathname: *const c_str, mode: *const c_str) -> *mut FILE;
safe fn malloc(size: usize) -> *mut c_void;
} Then we do the "remove the reasons" and "conflate trusted and unsafe" transformations and voila -- we arrived at the RFC proposal. You proposal removes every trace of |
So in the 2024 edition from what I can tell, you wouldn't be able to use
There is a pretty big difference between a language keyword, and a substring of a function name. |
@tmccombs I'm pretty sure |
I think #[doc = include_str!("../README.md")]
extern {} // there's no reason this should be unsafe and I expect extern type declarations to also not need unsafe. |
I'm saying
#![warn(unsafe_code)]
std::arch::global_asm! { "" }
// ^ using this macro is unsafe even though it does not need an `unsafe` block
Strawman. For Additionally, existing (≤2021) |
I understand. And I'm saying that's bad as one now has to know a list of things in Rust that implicitly introduce unsafety, as opposed to the simple rule that I don't think my
That should be rectified, like we did with unsafe attributes. No reason to repeat past mistakes. |
@rustbot labels -I-lang-nominated We discussed this in the lang triage meeting today, and we confirmed that:
|
Rendered
Continuation of #3439, very sorry for the mix up, but when I went to make #3477 I deleted my
rfcs
repo fork and re-forked. I had forgotten that there was an open PR at the time.