-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Unsafe derives and attributes #3715
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
base: master
Are you sure you want to change the base?
Changes from 7 commits
0530e44
aaeab26
41b758b
6e054cc
7a3e7ab
09fd6b2
82c2ad5
962e17c
2ecc61d
0c7684f
dfad26f
309cb97
bbed33f
6b5499c
04caa41
2b4e22b
dd1c9f0
102223a
fcc7e78
0549114
0c8a3cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,126 @@ | ||||||||
- Feature Name: `unsafe_derives_and_attrs` | ||||||||
- Start Date: 2024-10-22 | ||||||||
- RFC PR: [rust-lang/rfcs#3715](https://github.com/rust-lang/rfcs/pull/3715) | ||||||||
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) | ||||||||
|
||||||||
# Summary | ||||||||
[summary]: #summary | ||||||||
|
||||||||
Allow declaring proc macro attributes and derive macros as unsafe, and | ||||||||
requiring `unsafe` to invoke them. | ||||||||
|
||||||||
# Motivation | ||||||||
[motivation]: #motivation | ||||||||
|
||||||||
Some traits place requirements on implementations that the Rust compiler cannot | ||||||||
verify. Those traits can mark themselves as unsafe, requiring `unsafe impl` | ||||||||
syntax to implement. However, trait `derive` macros cannot currently require | ||||||||
`unsafe`. This RFC defines a syntax for declaring and using unsafe `derive` | ||||||||
macros. | ||||||||
|
||||||||
This RFC also defines a syntax for declaring proc macro attributes as unsafe. | ||||||||
|
||||||||
# Guide-level explanation | ||||||||
[guide-level-explanation]: #guide-level-explanation | ||||||||
|
||||||||
## Derives | ||||||||
|
||||||||
When declaring a proc macro `derive`, you can add the `unsafe` parameter to the | ||||||||
`proc_macro_derive` attribute to indicate that the derive requires `unsafe`: | ||||||||
|
||||||||
```rust | ||||||||
#[proc_macro_derive(DangerousTrait, unsafe)] | ||||||||
pub fn derive_helper_attr(_item: TokenStream) -> TokenStream { | ||||||||
TokenStream::new() | ||||||||
} | ||||||||
``` | ||||||||
|
||||||||
Invoking this derive requires writing `#[derive(unsafe(DangerousTrait))]`. | ||||||||
Invoking an unsafe derive without the unsafe derive syntax will produce a | ||||||||
compiler error. Using the unsafe derive syntax without an unsafe derive will | ||||||||
trigger an "unused unsafe" lint. | ||||||||
|
||||||||
A `proc_macro_derive` attribute can include both `attributes` for helper | ||||||||
attributes and `unsafe` to declare the derive unsafe, in any order. | ||||||||
|
||||||||
If writing code that enforces `SAFETY` comments for every use of `unsafe`, you | ||||||||
can write the `SAFETY` comment either prior to the derive (for a single unsafe | ||||||||
derive) or prior to the specific `unsafe(DangerousTrait)` in a list of derives: | ||||||||
|
||||||||
```rust | ||||||||
// SAFETY: ... | ||||||||
#[derive(unsafe(DangerousTrait))] | ||||||||
struct SomeStruct { ... } | ||||||||
|
||||||||
#[derive( | ||||||||
// SAFETY: ... | ||||||||
unsafe(DangerousTrait), | ||||||||
// SAFETY: ... | ||||||||
unsafe(AnotherDangerousTrait), | ||||||||
)] | ||||||||
struct AnotherStruct { ... } | ||||||||
``` | ||||||||
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
||||||||
## Attributes | ||||||||
|
||||||||
When declaring a proc macro attribute, you can add the `unsafe` parameter to | ||||||||
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
the `proc_macro_attribute` attribute to indicate that the attribute requires | ||||||||
`unsafe`: | ||||||||
|
||||||||
```rust | ||||||||
#[proc_macro_attribute(unsafe)] | ||||||||
pub fn dangerous(_attr: TokenStream, item: TokenStream) -> TokenStream { | ||||||||
item | ||||||||
} | ||||||||
``` | ||||||||
|
||||||||
Invoking an unsafe attribute requires the unsafe attribute syntax: | ||||||||
`#[unsafe(dangerous)]`. | ||||||||
|
||||||||
If writing code that enforces `SAFETY` comments for every use of `unsafe`, you | ||||||||
can write the `SAFETY` comment immediately prior to the attribute: | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
"What, you mean there's code somewhere that doesn't require that?" I'd prefer to not hint that this expectation is somehow not the default. Probably we can just leave this predicate out entirely. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @traviscross It is, in a very practical sense, not currently the default: there's no warn-by-default lint for it. I'm all for changing that, but meanwhile, it'd be inaccurate to imply that it is currently required everywhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Josh and I talked, but for others here, by wanting to remove the predicate, I don't mean to suggest by this language that it is the default or is required. If we say, "you can write X here," I don't think that implies that you must write "X" at all. I read it instead as, "if you're going to write an X, which is entirely your option as far as we're concerned, you can write it here." What I don't want to suggest is that writing such safety comments is unusual, and the fussy language referencing extrinsic requirements for doing this makes it feel that way to me. I proposed wording along the lines of "when adding a safety comment, you can write it here" that both Josh and I liked, and that I think he's planning to update the text to reflect. |
||||||||
|
||||||||
```rust | ||||||||
// SAFETY: ... | ||||||||
#[unsafe(dangerous)] | ||||||||
``` | ||||||||
|
||||||||
# Rationale and alternatives | ||||||||
[rationale-and-alternatives]: #rationale-and-alternatives | ||||||||
|
||||||||
This RFC proposes the synax `#[derive(unsafe(DangerousTrait))]`. We could, | ||||||||
instead, put the `unsafe` on the outside: `#[unsafe(derive(DangerousTrait))]`. | ||||||||
|
||||||||
Some rationale for putting it on the inside: | ||||||||
- This encourages minimizing the scope of the `unsafe`, isolating it to a | ||||||||
single trait. | ||||||||
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
- This allows writing all traits to derive within a single `#[derive(...)]`, if | ||||||||
desired. Putting the `unsafe` on the outside requires separate `derive`s for | ||||||||
safe and unsafe derives, and potentially multiple if the derives care about | ||||||||
ordering. | ||||||||
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
- This makes it easy to attach `SAFETY` comments to each individual trait. | ||||||||
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
- One way to think of `derive(Trait)` is `derive` is the mechanism to invoke | ||||||||
derive macros, and `Trait` is the actual derive macro. In this sense, when | ||||||||
writing `derive(unsafe(UnsafeTrait))`, the `unsafe` is attached to the | ||||||||
specific derive macro rather than the `derive` directive. | ||||||||
joshtriplett marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
||||||||
We could use a different syntax for invoking unsafe derives, such as | ||||||||
`derive(unsafe Trait)`. However, that would be inconsistent with unsafe | ||||||||
attributes (which use parentheses), *and* it has the potential to look like a | ||||||||
modifier to `Trait` (e.g. an unsafe version of `Trait`). | ||||||||
|
||||||||
# Prior art | ||||||||
[prior-art]: #prior-art | ||||||||
|
||||||||
RFC 3325 defined unsafe attributes. This RFC provides a natural extension of | ||||||||
that mechanism to derives. | ||||||||
|
||||||||
# Future possibilities | ||||||||
[future-possibilities]: #future-possibilities | ||||||||
|
||||||||
When we add support for `macro_rules!`-based attributes and derives, we should | ||||||||
provide a means for such attributes and derives to declare themselves unsafe as | ||||||||
well. | ||||||||
|
||||||||
We could provide a syntax to declare specific helper attributes of a derive as | ||||||||
unsafe, without declaring the entire derive unsafe. |
Uh oh!
There was an error while loading. Please reload this page.