From 0530e4420f74e922908e16338f29d25739ade013 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 22 Oct 2024 06:36:10 -0700 Subject: [PATCH 01/21] Unsafe derives and attributes --- text/0000-unsafe-derives-and-attrs.md | 87 +++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 text/0000-unsafe-derives-and-attrs.md diff --git a/text/0000-unsafe-derives-and-attrs.md b/text/0000-unsafe-derives-and-attrs.md new file mode 100644 index 00000000000..e95a52eb4af --- /dev/null +++ b/text/0000-unsafe-derives-and-attrs.md @@ -0,0 +1,87 @@ +- Feature Name: `unsafe_derives_and_attrs` +- Start Date: 2024-10-22 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- 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 either +`#[unsafe(derive(DangerousTrait))]` or `#[derive(unsafe(DangerousTrait))]`. +(The latter syntax allows isolating the `unsafe` to a single derive within a +list of derives.) 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. + +## 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)]`. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +Should we support the `#[unsafe(derive(DangerousTrait))]` syntax, or only +`#[derive(unsafe(DangerousTrait))]`? The former elevates the `unsafe` to be +more visible, and allows deriving several traits using one `unsafe`. The latter +isolates the `unsafe` to a specific trait. This RFC proposes supporting both, +but we could choose to only support the latter instead. + +# 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. From aaeab26d4b1261d118317d3548c18201f8a39169 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 22 Oct 2024 08:41:09 -0700 Subject: [PATCH 02/21] RFC 3715 --- ...fe-derives-and-attrs.md => 3715-unsafe-derives-and-attrs.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename text/{0000-unsafe-derives-and-attrs.md => 3715-unsafe-derives-and-attrs.md} (97%) diff --git a/text/0000-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md similarity index 97% rename from text/0000-unsafe-derives-and-attrs.md rename to text/3715-unsafe-derives-and-attrs.md index e95a52eb4af..5c3b8fb21d1 100644 --- a/text/0000-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -1,6 +1,6 @@ - Feature Name: `unsafe_derives_and_attrs` - Start Date: 2024-10-22 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- 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 From 41b758bf9f5caa1eec2581c958cb292f50a60869 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 23 Oct 2024 02:34:47 -0700 Subject: [PATCH 03/21] Mention alternative syntax and explain why we don't use it --- text/3715-unsafe-derives-and-attrs.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 5c3b8fb21d1..693dcad0a16 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -70,6 +70,11 @@ more visible, and allows deriving several traits using one `unsafe`. The latter isolates the `unsafe` to a specific trait. This RFC proposes supporting both, but we could choose to only support the latter instead. +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 From 6e054cc3a4afd41593be7ae5a593595684133cba Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 20 Nov 2024 10:31:31 -0800 Subject: [PATCH 04/21] Unresolved question for unsafe placement --- text/3715-unsafe-derives-and-attrs.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 693dcad0a16..0621d9c8f2e 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -81,6 +81,14 @@ modifier to `Trait` (e.g. an unsafe version of `Trait`). RFC 3325 defined unsafe attributes. This RFC provides a natural extension of that mechanism to derives. +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +This RFC proposes accepting both `#[unsafe(derive(MyTrait))]` and +`#[derive(unsafe(MyTrait))]`, among other reasons to make it easy to write +`#[derive(SafeTrait, unsafe(MyTrait))]`. Should we allow both, or only allow +the former? + # Future possibilities [future-possibilities]: #future-possibilities From 7a3e7abed112fc7da026a9ff590db80628bfabea Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 12 Mar 2025 18:02:25 -0700 Subject: [PATCH 05/21] Resolve the unresolved question about unsafe placement, and document rationale --- text/3715-unsafe-derives-and-attrs.md | 60 ++++++++++++++++++--------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 0621d9c8f2e..e388fdba5f2 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -35,16 +35,32 @@ pub fn derive_helper_attr(_item: TokenStream) -> TokenStream { } ``` -Invoking this derive requires writing either -`#[unsafe(derive(DangerousTrait))]` or `#[derive(unsafe(DangerousTrait))]`. -(The latter syntax allows isolating the `unsafe` to a single derive within a -list of derives.) 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. +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 @@ -61,14 +77,28 @@ pub fn dangerous(_attr: TokenStream, item: TokenStream) -> TokenStream { 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: + +```rust +/// SAFETY: ... +#[unsafe(dangerous)] +``` + # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -Should we support the `#[unsafe(derive(DangerousTrait))]` syntax, or only -`#[derive(unsafe(DangerousTrait))]`? The former elevates the `unsafe` to be -more visible, and allows deriving several traits using one `unsafe`. The latter -isolates the `unsafe` to a specific trait. This RFC proposes supporting both, -but we could choose to only support the latter instead. +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. We could use a different syntax for invoking unsafe derives, such as `derive(unsafe Trait)`. However, that would be inconsistent with unsafe @@ -81,14 +111,6 @@ modifier to `Trait` (e.g. an unsafe version of `Trait`). RFC 3325 defined unsafe attributes. This RFC provides a natural extension of that mechanism to derives. -# Unresolved questions -[unresolved-questions]: #unresolved-questions - -This RFC proposes accepting both `#[unsafe(derive(MyTrait))]` and -`#[derive(unsafe(MyTrait))]`, among other reasons to make it easy to write -`#[derive(SafeTrait, unsafe(MyTrait))]`. Should we allow both, or only allow -the former? - # Future possibilities [future-possibilities]: #future-possibilities From 09fd6b2e9912ab0c7176b618bd9f5150e6426582 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 13 Mar 2025 22:25:52 -0700 Subject: [PATCH 06/21] Fix SAFETY comments to be non-doc comments --- text/3715-unsafe-derives-and-attrs.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index e388fdba5f2..f5dd27ccf1c 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -48,14 +48,14 @@ 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: ... +// SAFETY: ... #[derive(unsafe(DangerousTrait))] struct SomeStruct { ... } #[derive( - /// SAFETY: ... + // SAFETY: ... unsafe(DangerousTrait), - /// SAFETY: ... + // SAFETY: ... unsafe(AnotherDangerousTrait), )] struct AnotherStruct { ... } @@ -81,7 +81,7 @@ If writing code that enforces `SAFETY` comments for every use of `unsafe`, you can write the `SAFETY` comment immediately prior to the attribute: ```rust -/// SAFETY: ... +// SAFETY: ... #[unsafe(dangerous)] ``` From 82c2ad5480481e82690fc07f450efe5c3ad15edd Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 13 Mar 2025 22:28:40 -0700 Subject: [PATCH 07/21] Add another rationale for `derive(unsafe(UnsafeTrait))`, from TC --- text/3715-unsafe-derives-and-attrs.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index f5dd27ccf1c..6a680b38946 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -99,6 +99,10 @@ Some rationale for putting it on the inside: 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 From 962e17c539e58e1cd2a0637c25e6b9e8cc49dcff Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 15 Mar 2025 17:30:11 -0700 Subject: [PATCH 08/21] Switch language to reference derive macros rather than traits Co-authored-by: Travis Cross --- text/3715-unsafe-derives-and-attrs.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 6a680b38946..567a131080f 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -93,16 +93,17 @@ 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. + single derive macro. +- This allows writing all derive macros to invoke 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 derive + macro. +- One way to think of `derive(Macro)` is that `derive(..)` enters a context in + which one or more derive macros can be invoked, and naming `Macro` is how we + actually invoke the derive macro. When invoking unsafe derive macros, we have + to wrap those with `unsafe(..)` as in `derive(unsafe(DangerousDeriveMacro))`. We could use a different syntax for invoking unsafe derives, such as `derive(unsafe Trait)`. However, that would be inconsistent with unsafe From 2ecc61d367a79c709ca33e576cbadc8fa10e3f1d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 15 Mar 2025 17:42:50 -0700 Subject: [PATCH 09/21] More wording updates from "trait" to "derive macro" --- text/3715-unsafe-derives-and-attrs.md | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 567a131080f..03d95e6eb94 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -29,34 +29,35 @@ 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 { +#[proc_macro_derive(DangerousDeriveMacro, unsafe)] +pub fn derive_dangerous_derive_macro(_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. +Invoking this derive macro requires writing +`#[derive(unsafe(DangerousDeriveMacro))]`. Invoking an unsafe derive macro +without the unsafe derive syntax will produce a compiler error. Using the +unsafe derive syntax without an unsafe derive macro 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: +derive) or prior to the specific `unsafe(DangerousDeriveMacro)` in a list of derives: ```rust // SAFETY: ... -#[derive(unsafe(DangerousTrait))] +#[derive(unsafe(DangerousDeriveMacro))] struct SomeStruct { ... } #[derive( // SAFETY: ... - unsafe(DangerousTrait), + unsafe(DangerousDeriveMacro), // SAFETY: ... - unsafe(AnotherDangerousTrait), + unsafe(AnotherDangerousDeriveMacro), )] struct AnotherStruct { ... } ``` @@ -88,8 +89,9 @@ can write the `SAFETY` comment immediately prior to the attribute: # 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))]`. +This RFC proposes the synax `#[derive(unsafe(DangerousDeriveMacro))]`. We +could, instead, put the `unsafe` on the outside: +`#[unsafe(derive(DangerousDeriveMacro))]`. Some rationale for putting it on the inside: - This encourages minimizing the scope of the `unsafe`, isolating it to a @@ -106,9 +108,11 @@ Some rationale for putting it on the inside: to wrap those with `unsafe(..)` as in `derive(unsafe(DangerousDeriveMacro))`. 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`). +`derive(unsafe DangerousDeriveMacro)`. However, that would be inconsistent with +unsafe attributes (which use parentheses), *and* it has the potential to look +like a modifier to `DangerousDeriveMacro` (e.g. an unsafe version of +`DangerousDeriveMacro`), particularly in the common case where +`DangerousDeriveMacro` has the same name as a trait. # Prior art [prior-art]: #prior-art From 0c7684fbfa43132ff76d49cbb42d02f660ee3d2f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 15 Mar 2025 17:47:05 -0700 Subject: [PATCH 10/21] Add alternative for restricting to one derive macro per `unsafe(derive(...))` --- text/3715-unsafe-derives-and-attrs.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 03d95e6eb94..ef000c43df8 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -107,6 +107,12 @@ Some rationale for putting it on the inside: actually invoke the derive macro. When invoking unsafe derive macros, we have to wrap those with `unsafe(..)` as in `derive(unsafe(DangerousDeriveMacro))`. +We could, *if* we used the `unsafe(derive(...))` syntax, additionally restrict +such derives to only contain a single trait, forcing the developer to only +invoke a single derive macro per `unsafe(derive(...))`. However, this syntax +naturally leads people to assume this would work, only to encounter an error +when they do the natural thing that seems like it should work. + We could use a different syntax for invoking unsafe derives, such as `derive(unsafe DangerousDeriveMacro)`. However, that would be inconsistent with unsafe attributes (which use parentheses), *and* it has the potential to look From dfad26fdef2b6247db636e2c143c42f0c707cc83 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 15 Mar 2025 17:51:20 -0700 Subject: [PATCH 11/21] Flag order-dependent derives as an unusual case --- text/3715-unsafe-derives-and-attrs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index ef000c43df8..fef79a40cdd 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -98,8 +98,8 @@ Some rationale for putting it on the inside: single derive macro. - This allows writing all derive macros to invoke 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. + separate `derive`s for safe and unsafe derives, and potentially multiple in + the (unusual) case where the derives care about ordering. - This makes it easy to attach `SAFETY` comments to each individual derive macro. - One way to think of `derive(Macro)` is that `derive(..)` enters a context in From 309cb9743b2958cd7132d226f476b8d514286953 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 15 Mar 2025 17:54:18 -0700 Subject: [PATCH 12/21] Add alternative for declaring with `proc_macro_derive(unsafe(DangerousDeriveMacro))` --- text/3715-unsafe-derives-and-attrs.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index fef79a40cdd..523157c9294 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -120,6 +120,11 @@ like a modifier to `DangerousDeriveMacro` (e.g. an unsafe version of `DangerousDeriveMacro`), particularly in the common case where `DangerousDeriveMacro` has the same name as a trait. +We could use a similar syntax for declaring unsafe derives as invoking them: +`proc_macro_derive(unsafe(DangerousDeriveMacro))`. However, because of the +precedent of `unsafe(attribute)`, this can be interpreted as being an unsafe +operation to *define* rather than an unsafe operation to *invoke*. + # Prior art [prior-art]: #prior-art From bbed33fbf32fad813287f194064226dd86673be0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 19 Mar 2025 10:25:39 -0700 Subject: [PATCH 13/21] Rewrite descriptions of SAFETY comments Use phrasings that can't be read as implying SAFETY comments are unusual. The previous phrasing could be interpreted as "in the unusual case where you're writing SAFETY comments ...". --- text/3715-unsafe-derives-and-attrs.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 523157c9294..4f9d7d2502b 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -44,9 +44,9 @@ 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(DangerousDeriveMacro)` in a list of derives: +When writing a `SAFETY` comment for each `unsafe`, you can place the `SAFETY` +comment either prior to the derive (for a single unsafe derive) or prior to the +specific `unsafe(DangerousDeriveMacro)` in a list of derives: ```rust // SAFETY: ... @@ -78,8 +78,8 @@ pub fn dangerous(_attr: TokenStream, item: TokenStream) -> TokenStream { 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: +When writing a `SAFETY` comment for each `unsafe`, you can place the `SAFETY` +comment immediately prior to the attribute: ```rust // SAFETY: ... From 6b5499c52ce37d8ff5a696291c2708e04ff2a3de Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 19 Mar 2025 11:54:08 -0700 Subject: [PATCH 14/21] Add note about rustfmt --- text/3715-unsafe-derives-and-attrs.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 4f9d7d2502b..ea7aab2b0c8 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -62,6 +62,10 @@ struct SomeStruct { ... } struct AnotherStruct { ... } ``` +(Note that current rustfmt will place every derive on a line of its own if any +have a comment. That could be changed in a future style edition, but this RFC +is not making or advocating any style proposals.) + ## Attributes When declaring a proc macro attribute, you can add the `unsafe` parameter to From 04caa4147b64a4068c0ce5c16dac55b402088a90 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 19 Mar 2025 14:56:35 -0700 Subject: [PATCH 15/21] Mention and discuss alternative of unsafe supertrait --- text/3715-unsafe-derives-and-attrs.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index ea7aab2b0c8..d7df493fc20 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -129,6 +129,14 @@ We could use a similar syntax for declaring unsafe derives as invoking them: precedent of `unsafe(attribute)`, this can be interpreted as being an unsafe operation to *define* rather than an unsafe operation to *invoke*. +If we didn't have this feature, a workaround trait authors could use for the +specific case of a derive macro implementing a trait is to have a separate +marker trait as a supertrait of the unsafe trait. Then, +`derive(DangerousTrait)` could require separately doing `unsafe impl +PrerequisiteForDangerousTrait`. This would achieve the goal of requiring +`unsafe` to appear somewhere when deriving the trait, but would not tie the two +together as directly or clearly. + # Prior art [prior-art]: #prior-art From 2b4e22b13022d77fd1ad2a2136694ab337d2e476 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 16 Apr 2025 10:24:26 -0700 Subject: [PATCH 16/21] Change syntax to `proc_macro_derive(unsafe(DangerousDeriveMacro))` --- text/3715-unsafe-derives-and-attrs.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index d7df493fc20..d70fb168afb 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -25,11 +25,11 @@ This RFC also defines a syntax for declaring proc macro attributes as unsafe. ## 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`: +When declaring a proc macro `derive`, you can use the following syntax to +indicate that the derive requires `unsafe`: ```rust -#[proc_macro_derive(DangerousDeriveMacro, unsafe)] +#[proc_macro_derive(unsafe(DangerousDeriveMacro)] pub fn derive_dangerous_derive_macro(_item: TokenStream) -> TokenStream { TokenStream::new() } @@ -124,10 +124,10 @@ like a modifier to `DangerousDeriveMacro` (e.g. an unsafe version of `DangerousDeriveMacro`), particularly in the common case where `DangerousDeriveMacro` has the same name as a trait. -We could use a similar syntax for declaring unsafe derives as invoking them: -`proc_macro_derive(unsafe(DangerousDeriveMacro))`. However, because of the -precedent of `unsafe(attribute)`, this can be interpreted as being an unsafe -operation to *define* rather than an unsafe operation to *invoke*. +We could use a different syntax for declaring unsafe derives, such as +`proc_macro_derive(DangerousDeriveMacro, unsafe)`. This would have the +advantage of not looking like the definition incurs an unsafe obligation, but +the disadvantage of using a different syntax for definition and use. If we didn't have this feature, a workaround trait authors could use for the specific case of a derive macro implementing a trait is to have a separate From dd1c9f01b6e73f7cc43bdd6f09f0d771cb6322cb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 16 Apr 2025 10:39:25 -0700 Subject: [PATCH 17/21] Expand motivation --- text/3715-unsafe-derives-and-attrs.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index d70fb168afb..b320bbbbeca 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -18,6 +18,14 @@ syntax to implement. However, trait `derive` macros cannot currently require `unsafe`. This RFC defines a syntax for declaring and using unsafe `derive` macros. +This provides value for any derive of an `unsafe trait`, ranging from standard +library unsafe traits such as `Send` and `Sync` to more complex unsafe traits +in the ecosystem. With this mechanism available, an `unsafe trait` has the +option of providing two different kinds of `derive` macros: a safe `derive` +macro that implements the `unsafe` trait in a fashion that's always safe (or +that fails if some obligation is not met), or an `unsafe` `derive` macro that +puts the safety obligation on the invoker of the `derive`. + This RFC also defines a syntax for declaring proc macro attributes as unsafe. # Guide-level explanation From 102223a03cea0a0693e3725e8d4656f0bf9e58f9 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 16 Apr 2025 10:41:02 -0700 Subject: [PATCH 18/21] Reorder attributes before derives --- text/3715-unsafe-derives-and-attrs.md | 48 +++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index b320bbbbeca..f4297791ec9 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -31,6 +31,30 @@ This RFC also defines a syntax for declaring proc macro attributes as unsafe. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation +## 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)]`. + +When writing a `SAFETY` comment for each `unsafe`, you can place the `SAFETY` +comment immediately prior to the attribute: + +```rust +// SAFETY: ... +#[unsafe(dangerous)] +``` + ## Derives When declaring a proc macro `derive`, you can use the following syntax to @@ -74,30 +98,6 @@ struct AnotherStruct { ... } have a comment. That could be changed in a future style edition, but this RFC is not making or advocating any style proposals.) -## 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)]`. - -When writing a `SAFETY` comment for each `unsafe`, you can place the `SAFETY` -comment immediately prior to the attribute: - -```rust -// SAFETY: ... -#[unsafe(dangerous)] -``` - # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From fcc7e7821a199f5b7abc618067c41f8647e1c8c5 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 16 Apr 2025 10:48:58 -0700 Subject: [PATCH 19/21] Add unsafe helper attributes --- text/3715-unsafe-derives-and-attrs.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index f4297791ec9..621faed0254 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -98,6 +98,24 @@ struct AnotherStruct { ... } have a comment. That could be changed in a future style edition, but this RFC is not making or advocating any style proposals.) +### Helper attributes + +A `derive` macro can have helper attributes. You can use the following syntax +to declare a helper attribute as `unsafe`: + +```rust +#[proc_macro_derive(MyDeriveMacro(, attributes(unsafe(dangerous_helper_attr))] +pub fn derive_my_derive_macro(_item: TokenStream) -> TokenStream { + TokenStream::new() +} +``` + +Invoking this helper attribute requires the unsafe attribute syntax: +`#[unsafe(dangerous_helper_attr)]`. + +Any combination of safe and unsafe attributes are allowed in both safe and +unsafe derive macros. + # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives @@ -157,6 +175,3 @@ that mechanism to derives. 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. From 054911476c16838bbfb7179ef55bf6a526ac4068 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 27 Apr 2025 12:02:32 +0100 Subject: [PATCH 20/21] Fix typo --- text/3715-unsafe-derives-and-attrs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index 621faed0254..e22d101103e 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -104,7 +104,7 @@ A `derive` macro can have helper attributes. You can use the following syntax to declare a helper attribute as `unsafe`: ```rust -#[proc_macro_derive(MyDeriveMacro(, attributes(unsafe(dangerous_helper_attr))] +#[proc_macro_derive(MyDeriveMacro, attributes(unsafe(dangerous_helper_attr))] pub fn derive_my_derive_macro(_item: TokenStream) -> TokenStream { TokenStream::new() } From 0c8a3cbb2fedc267ca732232a008a8844c116952 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 3 May 2025 12:05:12 +0100 Subject: [PATCH 21/21] RFC 3715: Expand motivation --- text/3715-unsafe-derives-and-attrs.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/text/3715-unsafe-derives-and-attrs.md b/text/3715-unsafe-derives-and-attrs.md index e22d101103e..923349b6744 100644 --- a/text/3715-unsafe-derives-and-attrs.md +++ b/text/3715-unsafe-derives-and-attrs.md @@ -26,6 +26,17 @@ macro that implements the `unsafe` trait in a fashion that's always safe (or that fails if some obligation is not met), or an `unsafe` `derive` macro that puts the safety obligation on the invoker of the `derive`. +Some examples of potential unsafe derives for `unsafe trait`s: + +- `TrustedLen` and `DerefPure` in the standard library. (Currently unstable, + but such a derive would be useful when they're stable, serving the function + of an `unsafe impl`.) +- `pyo3::marker::Ungil` in `pyo3`, in place of the current handling of a + blanket impl for any `Send` type. +- A hypothetical derive for `bytes::Buf` or similar. (For cases where a type + has fields or trivial expressions corresponding to the current slice and + offset.) + This RFC also defines a syntax for declaring proc macro attributes as unsafe. # Guide-level explanation