diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 2626631d800..fff1bcd72bc 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -30,6 +30,9 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim ast_passes_bad_c_variadic = only foreign or `unsafe extern "C"` functions may be C-variadic +ast_passes_bare_fn_invalid_safety = function pointers cannot be declared with `safe` safety qualifier + .suggestion = remove safe from this item + ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block .cannot_have = cannot have a body .invalid = the invalid body @@ -167,6 +170,9 @@ ast_passes_invalid_unnamed_field_ty = unnamed fields can only have struct or union types .label = not a struct or union +ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier + .suggestion = remove safe from this item + ast_passes_item_underscore = `{$kind}` items in this context need a name .label = `_` is not a valid name for this `{$kind}` item diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index cad1fc79d7f..e89b412687d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -456,15 +456,29 @@ impl<'a> AstValidator<'a> { } } - fn check_foreign_item_safety(&self, item_span: Span, safety: Safety) { - if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_)) - && (self.extern_mod_safety == Some(Safety::Default) - || !self.features.unsafe_extern_blocks) - { - self.dcx().emit_err(errors::InvalidSafetyOnExtern { - item_span, - block: self.current_extern_span(), - }); + fn check_item_safety(&self, span: Span, safety: Safety) { + match self.extern_mod_safety { + Some(extern_safety) => { + if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_)) + && (extern_safety == Safety::Default || !self.features.unsafe_extern_blocks) + { + self.dcx().emit_err(errors::InvalidSafetyOnExtern { + item_span: span, + block: self.current_extern_span(), + }); + } + } + None => { + if matches!(safety, Safety::Safe(_)) { + self.dcx().emit_err(errors::InvalidSafetyOnItem { span }); + } + } + } + } + + fn check_bare_fn_safety(&self, span: Span, safety: Safety) { + if matches!(safety, Safety::Safe(_)) { + self.dcx().emit_err(errors::InvalidSafetyOnBareFn { span }); } } @@ -746,6 +760,7 @@ impl<'a> AstValidator<'a> { fn visit_ty_common(&mut self, ty: &'a Ty) { match &ty.kind { TyKind::BareFn(bfty) => { + self.check_bare_fn_safety(bfty.decl_span, bfty.safety); self.check_fn_decl(&bfty.decl, SelfSemantic::No); Self::check_decl_no_pat(&bfty.decl, |span, _, _| { self.dcx().emit_err(errors::PatternFnPointer { span }); @@ -1174,11 +1189,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } } - ItemKind::Static(box StaticItem { expr: None, .. }) => { - self.dcx().emit_err(errors::StaticWithoutBody { - span: item.span, - replace_span: self.ending_semi_or_hi(item.span), - }); + ItemKind::Static(box StaticItem { expr, safety, .. }) => { + self.check_item_safety(item.span, *safety); + + if expr.is_none() { + self.dcx().emit_err(errors::StaticWithoutBody { + span: item.span, + replace_span: self.ending_semi_or_hi(item.span), + }); + } } ItemKind::TyAlias( ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. }, @@ -1212,7 +1231,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match &fi.kind { ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => { - self.check_foreign_item_safety(fi.span, sig.header.safety); self.check_defaultness(fi.span, *defaultness); self.check_foreign_fn_bodyless(fi.ident, body.as_deref()); self.check_foreign_fn_headerless(sig.header); @@ -1233,7 +1251,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_foreign_item_ascii_only(fi.ident); } ForeignItemKind::Static(box StaticItem { expr, safety, .. }) => { - self.check_foreign_item_safety(fi.span, *safety); + self.check_item_safety(fi.span, *safety); self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span)); self.check_foreign_item_ascii_only(fi.ident); } @@ -1453,6 +1471,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }; self.check_fn_decl(fk.decl(), self_semantic); + if let Some(&FnHeader { safety, .. }) = fk.header() { + self.check_item_safety(span, safety); + } + self.check_c_variadic_type(fk); // Functions cannot both be `const async` or `const gen` diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 601910ded20..96c476b271c 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -225,6 +225,20 @@ pub struct InvalidSafetyOnExtern { pub block: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_item_invalid_safety)] +pub struct InvalidSafetyOnItem { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_bare_fn_invalid_safety)] +pub struct InvalidSafetyOnBareFn { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bound_in_context)] pub struct BoundInContext<'a> { diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr index abaa6527b0a..29404f1793b 100644 --- a/tests/ui/parser/fn-header-semantic-fail.stderr +++ b/tests/ui/parser/fn-header-semantic-fail.stderr @@ -105,15 +105,6 @@ LL | extern "C" { LL | extern "C" fn fe4(); | ^^^^^^^^^^ help: remove this qualifier -error: items in unadorned `extern` blocks cannot have safety qualifiers - --> $DIR/fn-header-semantic-fail.rs:50:9 - | -LL | extern "C" { - | ---------- help: add unsafe to this `extern` block -... -LL | const async unsafe extern "C" fn fe5(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: functions in `extern` blocks cannot have qualifiers --> $DIR/fn-header-semantic-fail.rs:50:15 | @@ -141,6 +132,15 @@ LL | extern "C" { LL | const async unsafe extern "C" fn fe5(); | ^^^^^^^^^^ help: remove this qualifier +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/fn-header-semantic-fail.rs:50:9 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +... +LL | const async unsafe extern "C" fn fe5(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: functions cannot be both `const` and `async` --> $DIR/fn-header-semantic-fail.rs:50:9 | diff --git a/tests/ui/parser/no-const-fn-in-extern-block.stderr b/tests/ui/parser/no-const-fn-in-extern-block.stderr index 892024ce893..f575acc839d 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.stderr +++ b/tests/ui/parser/no-const-fn-in-extern-block.stderr @@ -6,6 +6,15 @@ LL | extern "C" { LL | const fn foo(); | ^^^^^ help: remove this qualifier +error: functions in `extern` blocks cannot have qualifiers + --> $DIR/no-const-fn-in-extern-block.rs:4:5 + | +LL | extern "C" { + | ---------- in this `extern` block +... +LL | const unsafe fn bar(); + | ^^^^^ help: remove this qualifier + error: items in unadorned `extern` blocks cannot have safety qualifiers --> $DIR/no-const-fn-in-extern-block.rs:4:5 | @@ -15,14 +24,5 @@ LL | extern "C" { LL | const unsafe fn bar(); | ^^^^^^^^^^^^^^^^^^^^^^ -error: functions in `extern` blocks cannot have qualifiers - --> $DIR/no-const-fn-in-extern-block.rs:4:5 - | -LL | extern "C" { - | ---------- in this `extern` block -... -LL | const unsafe fn bar(); - | ^^^^^ help: remove this qualifier - error: aborting due to 3 previous errors diff --git a/tests/ui/rust-2024/safe-outside-extern.gated.stderr b/tests/ui/rust-2024/safe-outside-extern.gated.stderr new file mode 100644 index 00000000000..ea7aa181445 --- /dev/null +++ b/tests/ui/rust-2024/safe-outside-extern.gated.stderr @@ -0,0 +1,32 @@ +error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:4:1 + | +LL | safe fn foo() {} + | ^^^^^^^^^^^^^^^^ + +error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:8:1 + | +LL | safe static FOO: i32 = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:13:5 + | +LL | safe fn foo(); + | ^^^^^^^^^^^^^^ + +error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:19:5 + | +LL | safe fn foo() {} + | ^^^^^^^^^^^^^^^^ + +error: function pointers cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:24:14 + | +LL | type FnPtr = safe fn(i32, i32) -> i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/rust-2024/safe-outside-extern.rs b/tests/ui/rust-2024/safe-outside-extern.rs new file mode 100644 index 00000000000..9ec0c5c70e1 --- /dev/null +++ b/tests/ui/rust-2024/safe-outside-extern.rs @@ -0,0 +1,28 @@ +//@ revisions: gated ungated +#![cfg_attr(gated, feature(unsafe_extern_blocks))] + +safe fn foo() {} +//~^ ERROR: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier +//[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658] + +safe static FOO: i32 = 1; +//~^ ERROR: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier +//[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658] + +trait Foo { + safe fn foo(); + //~^ ERROR: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + //[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658] +} + +impl Foo for () { + safe fn foo() {} + //~^ ERROR: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + //[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658] +} + +type FnPtr = safe fn(i32, i32) -> i32; +//~^ ERROR: function pointers cannot be declared with `safe` safety qualifier +//[ungated]~| ERROR: unsafe extern {}` blocks and `safe` keyword are experimental [E0658] + +fn main() {} diff --git a/tests/ui/rust-2024/safe-outside-extern.ungated.stderr b/tests/ui/rust-2024/safe-outside-extern.ungated.stderr new file mode 100644 index 00000000000..908f5b504eb --- /dev/null +++ b/tests/ui/rust-2024/safe-outside-extern.ungated.stderr @@ -0,0 +1,83 @@ +error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:4:1 + | +LL | safe fn foo() {} + | ^^^^^^^^^^^^^^^^ + +error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:8:1 + | +LL | safe static FOO: i32 = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:13:5 + | +LL | safe fn foo(); + | ^^^^^^^^^^^^^^ + +error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:19:5 + | +LL | safe fn foo() {} + | ^^^^^^^^^^^^^^^^ + +error: function pointers cannot be declared with `safe` safety qualifier + --> $DIR/safe-outside-extern.rs:24:14 + | +LL | type FnPtr = safe fn(i32, i32) -> i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental + --> $DIR/safe-outside-extern.rs:4:1 + | +LL | safe fn foo() {} + | ^^^^ + | + = note: see issue #123743 for more information + = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental + --> $DIR/safe-outside-extern.rs:8:1 + | +LL | safe static FOO: i32 = 1; + | ^^^^ + | + = note: see issue #123743 for more information + = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental + --> $DIR/safe-outside-extern.rs:13:5 + | +LL | safe fn foo(); + | ^^^^ + | + = note: see issue #123743 for more information + = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental + --> $DIR/safe-outside-extern.rs:19:5 + | +LL | safe fn foo() {} + | ^^^^ + | + = note: see issue #123743 for more information + = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental + --> $DIR/safe-outside-extern.rs:24:14 + | +LL | type FnPtr = safe fn(i32, i32) -> i32; + | ^^^^ + | + = note: see issue #123743 for more information + = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0658`.