about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFolkert de Vries <folkert@folkertdev.nl>2025-04-12 19:58:19 +0200
committerFolkert de Vries <folkert@folkertdev.nl>2025-04-13 01:22:59 +0200
commitf472cc8cd4c57687aac38fe3589818f1cc7956ba (patch)
treee7a243137c6d03c5aa9264c6d3a6cb62ead6679f
parent81d8c747fbdb17775dc4e107ad7e430e61a4e751 (diff)
downloadrust-f472cc8cd4c57687aac38fe3589818f1cc7956ba.tar.gz
rust-f472cc8cd4c57687aac38fe3589818f1cc7956ba.zip
error on unsafe attributes in pre-2024 editions
the `no_mangle`, `link_section` and `export_name` attributes are exceptions, and can still be used without an unsafe in earlier editions
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0755.md4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0756.md4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0757.md7
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs33
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs10
-rw-r--r--tests/codegen/cffi/ffi-const.rs2
-rw-r--r--tests/codegen/cffi/ffi-pure.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-ffi_const.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-ffi_const.stderr4
-rw-r--r--tests/ui/feature-gates/feature-gate-ffi_pure.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-ffi_pure.stderr4
-rw-r--r--tests/ui/ffi-attrs/ffi_const.rs11
-rw-r--r--tests/ui/ffi-attrs/ffi_const.stderr25
-rw-r--r--tests/ui/ffi-attrs/ffi_const2.rs4
-rw-r--r--tests/ui/ffi-attrs/ffi_const2.stderr4
-rw-r--r--tests/ui/ffi-attrs/ffi_pure.rs11
-rw-r--r--tests/ui/ffi-attrs/ffi_pure.stderr25
17 files changed, 102 insertions, 52 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md
index 88b7f484969..b67f078c78e 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0755.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0755.md
@@ -5,7 +5,7 @@ Erroneous code example:
 ```compile_fail,E0755
 #![feature(ffi_pure)]
 
-#[ffi_pure] // error!
+#[unsafe(ffi_pure)] // error!
 pub fn foo() {}
 # fn main() {}
 ```
@@ -17,7 +17,7 @@ side effects or infinite loops:
 #![feature(ffi_pure)]
 
 extern "C" {
-    #[ffi_pure] // ok!
+    #[unsafe(ffi_pure)] // ok!
     pub fn strlen(s: *const i8) -> isize;
 }
 # fn main() {}
diff --git a/compiler/rustc_error_codes/src/error_codes/E0756.md b/compiler/rustc_error_codes/src/error_codes/E0756.md
index ffdc421aab5..aadde038d12 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0756.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0756.md
@@ -6,7 +6,7 @@ Erroneous code example:
 ```compile_fail,E0756
 #![feature(ffi_const)]
 
-#[ffi_const] // error!
+#[unsafe(ffi_const)] // error!
 pub fn foo() {}
 # fn main() {}
 ```
@@ -18,7 +18,7 @@ which have no side effects except for their return value:
 #![feature(ffi_const)]
 
 extern "C" {
-    #[ffi_const] // ok!
+    #[unsafe(ffi_const)] // ok!
     pub fn strlen(s: *const i8) -> i32;
 }
 # fn main() {}
diff --git a/compiler/rustc_error_codes/src/error_codes/E0757.md b/compiler/rustc_error_codes/src/error_codes/E0757.md
index 41b06b23c4f..fb75b028f45 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0757.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0757.md
@@ -6,8 +6,9 @@ Erroneous code example:
 #![feature(ffi_const, ffi_pure)]
 
 extern "C" {
-    #[ffi_const]
-    #[ffi_pure] // error: `#[ffi_const]` function cannot be `#[ffi_pure]`
+    #[unsafe(ffi_const)]
+    #[unsafe(ffi_pure)]
+    //~^ ERROR `#[ffi_const]` function cannot be `#[ffi_pure]`
     pub fn square(num: i32) -> i32;
 }
 ```
@@ -19,7 +20,7 @@ As `ffi_const` provides stronger guarantees than `ffi_pure`, remove the
 #![feature(ffi_const)]
 
 extern "C" {
-    #[ffi_const]
+    #[unsafe(ffi_const)]
     pub fn square(num: i32) -> i32;
 }
 ```
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 1e33e2e9393..7615362e1f9 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -6,6 +6,7 @@ use AttributeDuplicates::*;
 use AttributeGate::*;
 use AttributeType::*;
 use rustc_data_structures::fx::FxHashMap;
+use rustc_span::edition::Edition;
 use rustc_span::{Symbol, sym};
 
 use crate::{Features, Stability};
@@ -65,9 +66,12 @@ pub enum AttributeSafety {
     /// Normal attribute that does not need `#[unsafe(...)]`
     Normal,
 
-    /// Unsafe attribute that requires safety obligations
-    /// to be discharged
-    Unsafe,
+    /// Unsafe attribute that requires safety obligations to be discharged.
+    ///
+    /// An error is emitted when `#[unsafe(...)]` is omitted, except when the attribute's edition
+    /// is less than the one stored in `unsafe_since`. This handles attributes that were safe in
+    /// earlier editions, but become unsafe in later ones.
+    Unsafe { unsafe_since: Option<Edition> },
 }
 
 #[derive(Clone, Copy)]
@@ -187,12 +191,23 @@ macro_rules! template {
 }
 
 macro_rules! ungated {
+    (unsafe($edition:ident) $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => {
+        BuiltinAttribute {
+            name: sym::$attr,
+            encode_cross_crate: $encode_cross_crate,
+            type_: $typ,
+            safety: AttributeSafety::Unsafe { unsafe_since: Some(Edition::$edition) },
+            template: $tpl,
+            gate: Ungated,
+            duplicates: $duplicates,
+        }
+    };
     (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => {
         BuiltinAttribute {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
             type_: $typ,
-            safety: AttributeSafety::Unsafe,
+            safety: AttributeSafety::Unsafe { unsafe_since: None },
             template: $tpl,
             gate: Ungated,
             duplicates: $duplicates,
@@ -217,7 +232,7 @@ macro_rules! gated {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
             type_: $typ,
-            safety: AttributeSafety::Unsafe,
+            safety: AttributeSafety::Unsafe { unsafe_since: None },
             template: $tpl,
             duplicates: $duplicates,
             gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate),
@@ -228,7 +243,7 @@ macro_rules! gated {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
             type_: $typ,
-            safety: AttributeSafety::Unsafe,
+            safety: AttributeSafety::Unsafe { unsafe_since: None },
             template: $tpl,
             duplicates: $duplicates,
             gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr),
@@ -423,9 +438,9 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
     ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No),
-    ungated!(unsafe export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
-    ungated!(unsafe link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
-    ungated!(unsafe no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
+    ungated!(unsafe(Edition2024) export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
+    ungated!(unsafe(Edition2024) link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
+    ungated!(unsafe(Edition2024) no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
     ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No),
     ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes),
 
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 6bbd650dcdf..6a1c2af48ed 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -157,7 +157,7 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte
 pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr: &Attribute) {
     let attr_item = attr.get_normal_item();
 
-    if safety == AttributeSafety::Unsafe {
+    if let AttributeSafety::Unsafe { unsafe_since } = safety {
         if let ast::Safety::Default = attr_item.unsafety {
             let path_span = attr_item.path.span;
 
@@ -167,7 +167,13 @@ pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr:
             // square bracket respectively.
             let diag_span = attr_item.span();
 
-            if attr.span.at_least_rust_2024() {
+            // Attributes can be safe in earlier editions, and become unsafe in later ones.
+            let emit_error = match unsafe_since {
+                None => true,
+                Some(unsafe_since) => attr.span.edition() >= unsafe_since,
+            };
+
+            if emit_error {
                 psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe {
                     span: path_span,
                     suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion {
diff --git a/tests/codegen/cffi/ffi-const.rs b/tests/codegen/cffi/ffi-const.rs
index 6c90902e89f..3ea9d517ec2 100644
--- a/tests/codegen/cffi/ffi-const.rs
+++ b/tests/codegen/cffi/ffi-const.rs
@@ -10,6 +10,6 @@ extern "C" {
     // CHECK-LABEL: declare{{.*}}void @foo()
     // CHECK-SAME: [[ATTRS:#[0-9]+]]
     // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}memory(none){{.*}} }
-    #[ffi_const]
+    #[unsafe(ffi_const)]
     pub fn foo();
 }
diff --git a/tests/codegen/cffi/ffi-pure.rs b/tests/codegen/cffi/ffi-pure.rs
index 2c5d5f5b4b1..a61e80ecf65 100644
--- a/tests/codegen/cffi/ffi-pure.rs
+++ b/tests/codegen/cffi/ffi-pure.rs
@@ -10,6 +10,6 @@ extern "C" {
     // CHECK-LABEL: declare{{.*}}void @foo()
     // CHECK-SAME: [[ATTRS:#[0-9]+]]
     // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}memory(read){{.*}} }
-    #[ffi_pure]
+    #[unsafe(ffi_pure)]
     pub fn foo();
 }
diff --git a/tests/ui/feature-gates/feature-gate-ffi_const.rs b/tests/ui/feature-gates/feature-gate-ffi_const.rs
index 9f3d783ccd6..35f91b99a6f 100644
--- a/tests/ui/feature-gates/feature-gate-ffi_const.rs
+++ b/tests/ui/feature-gates/feature-gate-ffi_const.rs
@@ -1,6 +1,6 @@
 #![crate_type = "lib"]
 
 extern "C" {
-    #[ffi_const] //~ ERROR the `#[ffi_const]` attribute is an experimental feature
+    #[unsafe(ffi_const)] //~ ERROR the `#[ffi_const]` attribute is an experimental feature
     pub fn foo();
 }
diff --git a/tests/ui/feature-gates/feature-gate-ffi_const.stderr b/tests/ui/feature-gates/feature-gate-ffi_const.stderr
index d083b826d6e..7e8c941be07 100644
--- a/tests/ui/feature-gates/feature-gate-ffi_const.stderr
+++ b/tests/ui/feature-gates/feature-gate-ffi_const.stderr
@@ -1,8 +1,8 @@
 error[E0658]: the `#[ffi_const]` attribute is an experimental feature
   --> $DIR/feature-gate-ffi_const.rs:4:5
    |
-LL |     #[ffi_const]
-   |     ^^^^^^^^^^^^
+LL |     #[unsafe(ffi_const)]
+   |     ^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #58328 <https://github.com/rust-lang/rust/issues/58328> for more information
    = help: add `#![feature(ffi_const)]` to the crate attributes to enable
diff --git a/tests/ui/feature-gates/feature-gate-ffi_pure.rs b/tests/ui/feature-gates/feature-gate-ffi_pure.rs
index b0dfa01ff4c..0f1288b234e 100644
--- a/tests/ui/feature-gates/feature-gate-ffi_pure.rs
+++ b/tests/ui/feature-gates/feature-gate-ffi_pure.rs
@@ -1,6 +1,6 @@
 #![crate_type = "lib"]
 
 extern "C" {
-    #[ffi_pure] //~ ERROR the `#[ffi_pure]` attribute is an experimental feature
+    #[unsafe(ffi_pure)] //~ ERROR the `#[ffi_pure]` attribute is an experimental feature
     pub fn foo();
 }
diff --git a/tests/ui/feature-gates/feature-gate-ffi_pure.stderr b/tests/ui/feature-gates/feature-gate-ffi_pure.stderr
index 6544d450eeb..cf923536d6c 100644
--- a/tests/ui/feature-gates/feature-gate-ffi_pure.stderr
+++ b/tests/ui/feature-gates/feature-gate-ffi_pure.stderr
@@ -1,8 +1,8 @@
 error[E0658]: the `#[ffi_pure]` attribute is an experimental feature
   --> $DIR/feature-gate-ffi_pure.rs:4:5
    |
-LL |     #[ffi_pure]
-   |     ^^^^^^^^^^^
+LL |     #[unsafe(ffi_pure)]
+   |     ^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #58329 <https://github.com/rust-lang/rust/issues/58329> for more information
    = help: add `#![feature(ffi_pure)]` to the crate attributes to enable
diff --git a/tests/ui/ffi-attrs/ffi_const.rs b/tests/ui/ffi-attrs/ffi_const.rs
index aa20a4d4c65..dddc862b0fa 100644
--- a/tests/ui/ffi-attrs/ffi_const.rs
+++ b/tests/ui/ffi-attrs/ffi_const.rs
@@ -1,15 +1,18 @@
 #![feature(ffi_const)]
 #![crate_type = "lib"]
 
-#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions
+#[unsafe(ffi_const)] //~ ERROR `#[ffi_const]` may only be used on foreign functions
 pub fn foo() {}
 
-#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions
+#[unsafe(ffi_const)] //~ ERROR `#[ffi_const]` may only be used on foreign functions
 macro_rules! bar {
-    () => ()
+    () => {};
 }
 
 extern "C" {
-    #[ffi_const] //~ ERROR `#[ffi_const]` may only be used on foreign functions
+    #[unsafe(ffi_const)] //~ ERROR `#[ffi_const]` may only be used on foreign functions
     static INT: i32;
+
+    #[ffi_const] //~ ERROR unsafe attribute used without unsafe
+    fn bar();
 }
diff --git a/tests/ui/ffi-attrs/ffi_const.stderr b/tests/ui/ffi-attrs/ffi_const.stderr
index 394b98f8971..7f31237539d 100644
--- a/tests/ui/ffi-attrs/ffi_const.stderr
+++ b/tests/ui/ffi-attrs/ffi_const.stderr
@@ -1,21 +1,32 @@
+error: unsafe attribute used without unsafe
+  --> $DIR/ffi_const.rs:16:7
+   |
+LL |     #[ffi_const]
+   |       ^^^^^^^^^ usage of unsafe attribute
+   |
+help: wrap the attribute in `unsafe(...)`
+   |
+LL |     #[unsafe(ffi_const)]
+   |       +++++++         +
+
 error[E0756]: `#[ffi_const]` may only be used on foreign functions
   --> $DIR/ffi_const.rs:4:1
    |
-LL | #[ffi_const]
-   | ^^^^^^^^^^^^
+LL | #[unsafe(ffi_const)]
+   | ^^^^^^^^^^^^^^^^^^^^
 
 error[E0756]: `#[ffi_const]` may only be used on foreign functions
   --> $DIR/ffi_const.rs:7:1
    |
-LL | #[ffi_const]
-   | ^^^^^^^^^^^^
+LL | #[unsafe(ffi_const)]
+   | ^^^^^^^^^^^^^^^^^^^^
 
 error[E0756]: `#[ffi_const]` may only be used on foreign functions
   --> $DIR/ffi_const.rs:13:5
    |
-LL |     #[ffi_const]
-   |     ^^^^^^^^^^^^
+LL |     #[unsafe(ffi_const)]
+   |     ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0756`.
diff --git a/tests/ui/ffi-attrs/ffi_const2.rs b/tests/ui/ffi-attrs/ffi_const2.rs
index 82fe8a9c91d..8a8de13b153 100644
--- a/tests/ui/ffi-attrs/ffi_const2.rs
+++ b/tests/ui/ffi-attrs/ffi_const2.rs
@@ -1,8 +1,8 @@
 #![feature(ffi_const, ffi_pure)]
 
 extern "C" {
-    #[ffi_pure] //~ ERROR `#[ffi_const]` function cannot be `#[ffi_pure]`
-    #[ffi_const]
+    #[unsafe(ffi_pure)] //~ ERROR `#[ffi_const]` function cannot be `#[ffi_pure]`
+    #[unsafe(ffi_const)]
     pub fn baz();
 }
 
diff --git a/tests/ui/ffi-attrs/ffi_const2.stderr b/tests/ui/ffi-attrs/ffi_const2.stderr
index b8cbc296370..d4c9bc42ec9 100644
--- a/tests/ui/ffi-attrs/ffi_const2.stderr
+++ b/tests/ui/ffi-attrs/ffi_const2.stderr
@@ -1,8 +1,8 @@
 error[E0757]: `#[ffi_const]` function cannot be `#[ffi_pure]`
   --> $DIR/ffi_const2.rs:4:5
    |
-LL |     #[ffi_pure]
-   |     ^^^^^^^^^^^
+LL |     #[unsafe(ffi_pure)]
+   |     ^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/ffi-attrs/ffi_pure.rs b/tests/ui/ffi-attrs/ffi_pure.rs
index 6d2f3a614ec..1f4812f55cf 100644
--- a/tests/ui/ffi-attrs/ffi_pure.rs
+++ b/tests/ui/ffi-attrs/ffi_pure.rs
@@ -1,15 +1,18 @@
 #![feature(ffi_pure)]
 #![crate_type = "lib"]
 
-#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
+#[unsafe(ffi_pure)] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
 pub fn foo() {}
 
-#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
+#[unsafe(ffi_pure)] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
 macro_rules! bar {
-    () => ()
+    () => {};
 }
 
 extern "C" {
-    #[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
+    #[unsafe(ffi_pure)] //~ ERROR `#[ffi_pure]` may only be used on foreign functions
     static INT: i32;
+
+    #[ffi_pure] //~ ERROR unsafe attribute used without unsafe
+    fn bar();
 }
diff --git a/tests/ui/ffi-attrs/ffi_pure.stderr b/tests/ui/ffi-attrs/ffi_pure.stderr
index 8b61a4b609f..bd1177c01e2 100644
--- a/tests/ui/ffi-attrs/ffi_pure.stderr
+++ b/tests/ui/ffi-attrs/ffi_pure.stderr
@@ -1,21 +1,32 @@
+error: unsafe attribute used without unsafe
+  --> $DIR/ffi_pure.rs:16:7
+   |
+LL |     #[ffi_pure]
+   |       ^^^^^^^^ usage of unsafe attribute
+   |
+help: wrap the attribute in `unsafe(...)`
+   |
+LL |     #[unsafe(ffi_pure)]
+   |       +++++++        +
+
 error[E0755]: `#[ffi_pure]` may only be used on foreign functions
   --> $DIR/ffi_pure.rs:4:1
    |
-LL | #[ffi_pure]
-   | ^^^^^^^^^^^
+LL | #[unsafe(ffi_pure)]
+   | ^^^^^^^^^^^^^^^^^^^
 
 error[E0755]: `#[ffi_pure]` may only be used on foreign functions
   --> $DIR/ffi_pure.rs:7:1
    |
-LL | #[ffi_pure]
-   | ^^^^^^^^^^^
+LL | #[unsafe(ffi_pure)]
+   | ^^^^^^^^^^^^^^^^^^^
 
 error[E0755]: `#[ffi_pure]` may only be used on foreign functions
   --> $DIR/ffi_pure.rs:13:5
    |
-LL |     #[ffi_pure]
-   |     ^^^^^^^^^^^
+LL |     #[unsafe(ffi_pure)]
+   |     ^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0755`.