-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC for unsafe blocks in unsafe fn #2585
RFC for unsafe blocks in unsafe fn #2585
Conversation
If this were to be accepted, it would be much better to get the warning in before the 2018 epoch hits stable. If there's no time to get it in for 2018 then I don't think it should be accepted. |
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
This new warning will likely fire for the vast majority of `unsafe fn` out there. |
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.
We can start as allow
by default. The fact that const unsafe fn
already behaves this way and that clippy
can uplift this lint to warn
, will already make sure to migrate large parts of the ecosystem.
Oh... you are already mentioning that below in the unresolved questions...
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.
Yeah, I had to follow the RFC structure, didn't I? ;)
@Diggsey There will be another edition. And once we no longer warn about |
We needed this years ago, so the sooner the better. |
# Prior art | ||
[prior-art]: #prior-art | ||
|
||
None that I am aware of: Other languages do not have `unsafe` blocks. |
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.
C# has unsafe
blocks in addition to unsafe
methods. Though it's not super helpful since I'm not aware of the C# community ever discussing burden of proof issues like this RFC does, probably because 99.99% of the time the answer in that language is "unsafe just isn't worth it". I couldn't even find a C# style guide that mentions the existence of unsafe code, much less has guidelines for making it less dangerous.
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.
Furthermore, while other languages may not have blocks many do have escape hatches (which are relevant to this section)... Examples I found:
-
Haskell: http://hackage.haskell.org/package/base-4.12.0.0/docs/System-IO-Unsafe.html#v:unsafePerformIO and http://hackage.haskell.org/package/base-4.8.0.0/docs/Unsafe-Coerce.html
-
Idris: https://github.com/idris-lang/Idris-dev/blob/master/libs/prelude/Builtins.idr#L189
-
Agda: https://agda.readthedocs.io/en/v2.5.4.2/language/postulates.html
-
Coq: https://github.com/coq/coq/wiki/CoqAndAxioms also, http://adam.chlipala.net/cpdt/html/Universes.html#lab78
-
ReasonML: https://reasonml.github.io/docs/en/interop -- actually, this is sort of a block...
-
OCaml: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Obj.html
-
Ada: http://www.adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-1.html
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.
@Centril But this RFC is specifically about blocks and nesting of unsafe escape hatches. I do not think any of the examples you mention apply there, do they?
@Ixrec Thanks, I had no idea! And it looks like unsafe operations can be used freely in unsafe functions. :/
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.
@RalfJung not with that specificity no; the languages have the "block" form, e.g.:
x = unsafePerformIO $ do
foo
bar
...
what they lack is the unsafe function form.
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.
That's still quite different from Rust. It's just a normal function to the compiler, no checks for "unsafe operations" or so are performed. I do not see a close enough relation to this RFC.
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.
@RalfJung alright; fair enough. Let's leave this bit (the comment thread) open for interested readers who want to see the associated material linked. :)
[drawbacks]: #drawbacks | ||
|
||
This new warning will likely fire for the vast majority of `unsafe fn` out there. | ||
|
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.
Other possible drawbacks to list:
-
It will become less ergonomic to write unsafe code (it's justified I think, but worth mentioning...).
-
People might just do this:
unsafe fn frobnicate(x: T, y: U, ...) -> R {
unsafe {
... // Actual code.
}
}
and then nothing has been gained. I don't know what the risk of this is, but worth mentioning.
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 added (a variant of) 1.
For 2., I think something has been gained: It is not possible to incrementally improve this function's unsafe
locality. Or maybe it is not worth it, then that has at least been explicitly documented in the code.
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.
Yeah I'm not entirely sure 2. is a drawback or not; I usually try to write the section as what someone else might think is a potential drawback (but not necessarily me) -- i.e. this is the section where I try to bring out my inner Dr. Phil / empathy =P
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.
The drawbacks section now says
Many
unsafe fn
are actually rather short (no more than 3 lines) and will
likely end up just being one largeunsafe
block. This change would make such functions less ergonomic to write.
That should cover 2, right?
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.
@RalfJung the concern is actually slightly different here; it is that people will just go and write one big unsafe { ... }
when they shouldn't.
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.
Isn't that already possible today?
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.
@mark-i-m yes; sure -- the concern is that the change we do here might not have any noticable effect cause people could be lazy and...
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.
Wouldn't it be good that people can explicitly choose to not write a big unsafe
block instead of being forced into one automatically?
I've added T-dev-tools for the possible clippy lint for now. If no such clippy lint is proposed in the final version before final review of the RFC I'll remove that team. |
I think this RFC need more examples of realistic code where this would help, and an explanation of why it helps in enough cases that it's worse the obvious pain in current cases. That seems especially true since "safe" code is just as suspect when it's around Another alternative: an More generally, |
@scottmcm There are some places in the language where you are forced to use Here are some example from a project I worked on:
|
Another concrete use case I find valuable: Callback functions used when interacting with C libraries are almost always |
There is some syntactical similarity between |
well... not everyone agrees (as you know) ;) https://internals.rust-lang.org/t/what-does-unsafe-mean/6696/2 |
Some further examples of longer
Basically any time your However, I also have to admit that the vast majority of |
I am aware. However, I gave my usual arguments above, and AFAIK they have not been refuted yet, so I will keep claiming that everyone who says I think whoever claims that But anyway, this is getting off-topic. ;) |
I’m worried about the migration of existing code. I’d like to see this RFC make it a requirement that But this is tricky, simply wrapping the entire body of a function into a new |
I wouldn't necessarily say so. It still provides benefits for new unsafe code written later, and it permits gradual migration of existing unsafe code. |
Good point, my "sort of" only applies to existing code. I didn’t meant to argue against this RFC, I was only pondering the merits of different ways to deal with the migration. Sorry if I implied otherwise. |
It's okay. :) I will add something about migrations to the RFC text. |
I wonder how many of existing unsafe fn foo() {
unsafe {
// ..
}
} And while treating |
Given that this didn't happen, devtools should be ignored here. Going to tick all our boxes and untag the team (which seems to be the way to handle this without re-FCPing, the "majority of reviewers" calculation will take this into account) |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
implicitly treated like a block has made it hard to realize this duality | ||
[even for experienced Rust developers][unsafe-dual]. (Completing the picture, | ||
`unsafe Trait` also defines an obligation, that is discharged by `unsafe impl`. | ||
Curiously, `unsafe trait` does *not* implicitly make all bodies of default |
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 don't think this is curious at all. Or at least, I think its very easy to justify: The methods of an unsafe trait
are not themselves unsafe fn
. So it would be unsound to make their default method definitions implicitly have unsafe
bodies.
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.
Rationale: We have an unsafe
keyword associated to an item with a body (trait
, impl
, or fn
in this case), should the body be logically surrounded by a unsafe
block? (all items in the trait
/impl
or all statements in a fn
). Currently the answer differs between trait
/impl
and fn
, and that's curious.
If we apply your logic to unsafe fn
, we shouldn't be wrapping the body of an unsafe fn
with an unsafe
block, because that would be unsound (an unsafe
fn is requires the caller to enforce invariants, and says nothing about the callee).
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.
So it would be unsound to make their default method definitions implicitly have unsafe bodies.
I think this depends on the particular definition of "unsound". unsafe
is still required to cause any UB. And the boy of a fn
inside an unsafe impl
already does not to be manually reviewed to avoid UB elsewhere (in callers that rely on the unsafe part of the contract).
I agree with you for our conventional definition of "unsound", though.
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC will be merged soon. |
Huzzah! The @rust-lang/lang team has decided to accept this RFC. If you'd like to follow its progess towards implementation, please subscribe to the tracking issue. |
This is a warning now that rust-lang/rfcs#2585 has landed.
I really hope this doesn't become an error in the future, and to be honest I would even be sad to see it be deny-by-default. When the scope of unsafe code is significantly larger than a function, such as for modules full of fiddling with hardware through memory-mapped I/O and such, the verbosity is quite extreme. This seems to me like it punishes breaking up unsafe code into more functions. |
Hard Error would be completely unacceptable. Deny By Default wouldn't be a big deal. You just put a single |
Using a slice of elements that are not properly initialized is UB. Although new_uninitialized_sliced is a private function, mark it as unsafe to make clear that the caller has the responsibility to initialize all elements of the slice without looking at the old contents. For now we need to suppress warnings about the use of unsafe blocks in an unsafe function. This will be changed based on RFC 2585. Marking a function as unsafe and using unsafe code within that function should and will be two different things: rust-lang/rfcs#2585
RFC #2585 has been updated to be an optional lint for now. We clearly want this behavior, so use this lint for our code. While here, also enable some other lints that sound useful. We are actually missing a couple of unsafe{} blocks in unsafe functions, so follow-up commits are needed to properly document the safety assumptions around these. See: rust-lang/rfcs#2585 (comment) See: rust-lang/rust#71668
No longer treat the body of an
unsafe fn
as being anunsafe
block. To avoid a breaking change, this is a warning now and may become an error in a future edition.Rendered
The RFC has been adjusted to be a lint; see here for the comment announcing that change and the following discussion.
Cc @rust-lang/wg-unsafe-code-guidelines