Skip to content

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

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0530e44
Unsafe derives and attributes
joshtriplett Oct 22, 2024
aaeab26
RFC 3715
joshtriplett Oct 22, 2024
41b758b
Mention alternative syntax and explain why we don't use it
joshtriplett Oct 23, 2024
6e054cc
Unresolved question for unsafe placement
joshtriplett Nov 20, 2024
7a3e7ab
Resolve the unresolved question about unsafe placement, and document …
joshtriplett Mar 13, 2025
09fd6b2
Fix SAFETY comments to be non-doc comments
joshtriplett Mar 14, 2025
82c2ad5
Add another rationale for `derive(unsafe(UnsafeTrait))`, from TC
joshtriplett Mar 14, 2025
962e17c
Switch language to reference derive macros rather than traits
joshtriplett Mar 16, 2025
2ecc61d
More wording updates from "trait" to "derive macro"
joshtriplett Mar 16, 2025
0c7684f
Add alternative for restricting to one derive macro per `unsafe(deriv…
joshtriplett Mar 16, 2025
dfad26f
Flag order-dependent derives as an unusual case
joshtriplett Mar 16, 2025
309cb97
Add alternative for declaring with `proc_macro_derive(unsafe(Dangerou…
joshtriplett Mar 16, 2025
bbed33f
Rewrite descriptions of SAFETY comments
joshtriplett Mar 19, 2025
6b5499c
Add note about rustfmt
joshtriplett Mar 19, 2025
04caa41
Mention and discuss alternative of unsafe supertrait
joshtriplett Mar 19, 2025
2b4e22b
Change syntax to `proc_macro_derive(unsafe(DangerousDeriveMacro))`
joshtriplett Apr 16, 2025
dd1c9f0
Expand motivation
joshtriplett Apr 16, 2025
102223a
Reorder attributes before derives
joshtriplett Apr 16, 2025
fcc7e78
Add unsafe helper attributes
joshtriplett Apr 16, 2025
0549114
Fix typo
joshtriplett Apr 27, 2025
0c8a3cb
RFC 3715: Expand motivation
joshtriplett May 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions text/3715-unsafe-derives-and-attrs.md
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 { ... }
```

## Attributes

When declaring a proc macro attribute, you can add the `unsafe` parameter to
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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If writing code that enforces `SAFETY` comments for every use of `unsafe`, you
can write the `SAFETY` comment immediately prior to the attribute:
You can write the `SAFETY` comment immediately prior to the attribute:

"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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

@traviscross traviscross Mar 17, 2025

Choose a reason for hiding this comment

The 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.
- 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.
- This makes it easy to attach `SAFETY` comments to each individual trait.
- 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.

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.