diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7a45d909d0779..910fbb8769741 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2501,6 +2501,8 @@ pub enum IsAuto { pub enum Safety { /// `unsafe` an item is explicitly marked as `unsafe`. Unsafe(Span), + /// `safe` an item is explicitly marked as `safe`. + Safe(Span), /// Default means no value was provided, it will take a default value given the context in /// which is used. Default, @@ -3162,6 +3164,7 @@ pub struct DelegationMac { #[derive(Clone, Encodable, Decodable, Debug)] pub struct StaticItem { pub ty: P, + pub safety: Safety, pub mutability: Mutability, pub expr: Option>, } @@ -3171,6 +3174,7 @@ pub struct StaticItem { #[derive(Clone, Encodable, Decodable, Debug)] pub struct StaticForeignItem { pub ty: P, + pub safety: Safety, pub mutability: Mutability, pub expr: Option>, } @@ -3179,6 +3183,7 @@ impl From for StaticForeignItem { fn from(static_item: StaticItem) -> StaticForeignItem { StaticForeignItem { ty: static_item.ty, + safety: static_item.safety, mutability: static_item.mutability, expr: static_item.expr, } @@ -3189,6 +3194,7 @@ impl From for StaticItem { fn from(static_item: StaticForeignItem) -> StaticItem { StaticItem { ty: static_item.ty, + safety: static_item.safety, mutability: static_item.mutability, expr: static_item.expr, } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 5c581c270e498..a04c648ac731f 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -862,6 +862,7 @@ fn visit_defaultness(defaultness: &mut Defaultness, vis: &mut T) fn visit_safety(safety: &mut Safety, vis: &mut T) { match safety { Safety::Unsafe(span) => vis.visit_span(span), + Safety::Safe(span) => vis.visit_span(span), Safety::Default => {} } } @@ -1079,7 +1080,7 @@ impl NoopVisitItemKind for ItemKind { match self { ItemKind::ExternCrate(_orig_name) => {} ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree), - ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => { + ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => { vis.visit_ty(ty); visit_opt(expr, |expr| vis.visit_expr(expr)); } @@ -1289,7 +1290,12 @@ pub fn noop_flat_map_item( impl NoopVisitItemKind for ForeignItemKind { fn noop_visit(&mut self, visitor: &mut impl MutVisitor) { match self { - ForeignItemKind::Static(box StaticForeignItem { ty, mutability: _, expr }) => { + ForeignItemKind::Static(box StaticForeignItem { + ty, + mutability: _, + expr, + safety: _, + }) => { visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 099a6096d0b5d..109c401bb6a20 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -210,6 +210,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> boo kw::Unsafe, kw::While, kw::Yield, + kw::Safe, kw::Static, ] .contains(&name) @@ -577,6 +578,7 @@ impl Token { kw::Impl, kw::Unsafe, kw::Const, + kw::Safe, kw::Static, kw::Union, kw::Macro, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index b2f3b27c77e90..de285aed16567 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -334,7 +334,7 @@ impl WalkItemKind for ItemKind { match self { ItemKind::ExternCrate(_) => {} ItemKind::Use(use_tree) => try_visit!(visitor.visit_use_tree(use_tree, item.id, false)), - ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => { + ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } @@ -658,7 +658,12 @@ impl WalkItemKind for ForeignItemKind { ) -> V::Result { let &Item { id, span, ident, ref vis, .. } = item; match self { - ForeignItemKind::Static(box StaticForeignItem { ty, mutability: _, expr }) => { + ForeignItemKind::Static(box StaticForeignItem { + ty, + mutability: _, + expr, + safety: _, + }) => { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a15449409df3a..8c963e9f8907a 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -181,7 +181,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs) } - ItemKind::Static(box ast::StaticItem { ty: t, mutability: m, expr: e }) => { + ItemKind::Static(box ast::StaticItem { ty: t, safety: _, mutability: m, expr: e }) => { let (ty, body_id) = self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy); hir::ItemKind::Static(ty, *m, body_id) @@ -388,7 +388,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)), }; hir::ItemKind::Impl(self.arena.alloc(hir::Impl { - safety: self.lower_safety(*safety), + safety: self.lower_safety(*safety, hir::Safety::Safe), polarity, defaultness, defaultness_span, @@ -418,7 +418,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let items = this.arena.alloc_from_iter( items.iter().map(|item| this.lower_trait_item_ref(item)), ); - let safety = this.lower_safety(*safety); + let safety = this.lower_safety(*safety, hir::Safety::Safe); (safety, items, bounds) }, ); @@ -660,13 +660,21 @@ impl<'hir> LoweringContext<'_, 'hir> { this.lower_fn_params_to_names(fdec), ) }); + let safety = self.lower_safety(sig.header.safety, hir::Safety::Unsafe); - hir::ForeignItemKind::Fn(fn_dec, fn_args, generics) + hir::ForeignItemKind::Fn(fn_dec, fn_args, generics, safety) } - ForeignItemKind::Static(box StaticForeignItem { ty, mutability, expr: _ }) => { + ForeignItemKind::Static(box StaticForeignItem { + ty, + mutability, + expr: _, + safety, + }) => { let ty = self .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); - hir::ForeignItemKind::Static(ty, *mutability) + let safety = self.lower_safety(*safety, hir::Safety::Unsafe); + + hir::ForeignItemKind::Static(ty, *mutability, safety) } ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type, ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"), @@ -1360,7 +1368,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::IsAsync::NotAsync }; hir::FnHeader { - safety: self.lower_safety(h.safety), + safety: self.lower_safety(h.safety, hir::Safety::Safe), asyncness: asyncness, constness: self.lower_constness(h.constness), abi: self.lower_extern(h.ext), @@ -1410,10 +1418,11 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - pub(super) fn lower_safety(&mut self, s: Safety) -> hir::Safety { + pub(super) fn lower_safety(&mut self, s: Safety, default: hir::Safety) -> hir::Safety { match s { Safety::Unsafe(_) => hir::Safety::Unsafe, - Safety::Default => hir::Safety::Safe, + Safety::Default => default, + Safety::Safe(_) => hir::Safety::Safe, } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5a80fa803f840..023dc6d52c3eb 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1321,7 +1321,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy { generic_params, - safety: self.lower_safety(f.safety), + safety: self.lower_safety(f.safety, hir::Safety::Safe), abi: self.lower_extern(f.ext), decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None), param_names: self.lower_fn_params_to_names(&f.decl), diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 3a4c95b250c31..9a8689e27c068 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -67,6 +67,9 @@ ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have quali .label = in this `extern` block .suggestion = remove this qualifier +ast_passes_extern_invalid_safety = items in unadorned `extern` blocks cannot have safety qualifiers + .suggestion = add unsafe to this `extern` block + ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers .label = in this `extern` block .note = this limitation may be lifted in the future; see issue #83942 for more information @@ -174,6 +177,8 @@ ast_passes_match_arm_with_no_body = `match` arm with no body .suggestion = add a body after the pattern +ast_passes_missing_unsafe_on_extern = extern blocks must be unsafe + ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name .help = consider using the `#[path]` attribute to specify filesystem path diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 435b0b6a95657..0fbb288cc968c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -15,7 +15,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_feature::Features; use rustc_parse::validate_attr; use rustc_session::lint::builtin::{ - DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY, + DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, + PATTERNS_IN_FNS_WITHOUT_BODY, }; use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_session::Session; @@ -86,6 +87,9 @@ struct AstValidator<'a> { /// or `Foo::Bar` is_impl_trait_banned: bool, + /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe. + extern_mod_safety: Option, + lint_buffer: &'a mut LintBuffer, } @@ -116,6 +120,12 @@ impl<'a> AstValidator<'a> { self.outer_trait_or_trait_impl = old; } + fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety)); + f(self); + self.extern_mod_safety = old; + } + fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) { let old = mem::replace(&mut self.is_impl_trait_banned, true); f(self); @@ -429,6 +439,18 @@ 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_defaultness(&self, span: Span, defaultness: Defaultness) { if let Defaultness::Default(def_span) = defaultness { let span = self.session.source_map().guess_head_span(span); @@ -518,7 +540,7 @@ impl<'a> AstValidator<'a> { fn check_foreign_fn_headerless( &self, // Deconstruct to ensure exhaustiveness - FnHeader { safety, coroutine_kind, constness, ext }: FnHeader, + FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader, ) { let report_err = |span| { self.dcx().emit_err(errors::FnQualifierInExtern { @@ -526,10 +548,6 @@ impl<'a> AstValidator<'a> { block: self.current_extern_span(), }); }; - match safety { - Safety::Unsafe(span) => report_err(span), - Safety::Default => (), - } match coroutine_kind { Some(knd) => report_err(knd.span()), None => (), @@ -1017,19 +1035,39 @@ impl<'a> Visitor<'a> for AstValidator<'a> { return; // Avoid visiting again. } ItemKind::ForeignMod(ForeignMod { abi, safety, .. }) => { - let old_item = mem::replace(&mut self.extern_mod, Some(item)); - self.visibility_not_permitted( - &item.vis, - errors::VisibilityNotPermittedNote::IndividualForeignItems, - ); - if let &Safety::Unsafe(span) = safety { - self.dcx().emit_err(errors::UnsafeItem { span, kind: "extern block" }); - } - if abi.is_none() { - self.maybe_lint_missing_abi(item.span, item.id); - } - visit::walk_item(self, item); - self.extern_mod = old_item; + self.with_in_extern_mod(*safety, |this| { + let old_item = mem::replace(&mut this.extern_mod, Some(item)); + this.visibility_not_permitted( + &item.vis, + errors::VisibilityNotPermittedNote::IndividualForeignItems, + ); + + if this.features.unsafe_extern_blocks { + if &Safety::Default == safety { + if item.span.at_least_rust_2024() { + this.dcx() + .emit_err(errors::MissingUnsafeOnExtern { span: item.span }); + } else { + this.lint_buffer.buffer_lint( + MISSING_UNSAFE_ON_EXTERN, + item.id, + item.span, + BuiltinLintDiag::MissingUnsafeOnExtern { + suggestion: item.span.shrink_to_lo(), + }, + ); + } + } + } else if let &Safety::Unsafe(span) = safety { + this.dcx().emit_err(errors::UnsafeItem { span, kind: "extern block" }); + } + + if abi.is_none() { + this.maybe_lint_missing_abi(item.span, item.id); + } + visit::walk_item(this, item); + this.extern_mod = old_item; + }); return; // Avoid visiting again. } ItemKind::Enum(def, _) => { @@ -1161,6 +1199,7 @@ 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); @@ -1180,7 +1219,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_foreign_ty_genericless(generics, where_clauses); self.check_foreign_item_ascii_only(fi.ident); } - ForeignItemKind::Static(box StaticForeignItem { ty: _, mutability: _, expr }) => { + ForeignItemKind::Static(box StaticForeignItem { expr, safety, .. }) => { + self.check_foreign_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); } @@ -1736,6 +1776,7 @@ pub fn check_crate( outer_impl_trait: None, disallow_tilde_const: Some(DisallowTildeConstContext::Item), is_impl_trait_banned: false, + extern_mod_safety: None, lint_buffer: lints, }; visit::walk_crate(&mut validator, krate); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index c07fbe5b0166e..260c182bd9e49 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -216,6 +216,15 @@ pub enum ExternBlockSuggestion { }, } +#[derive(Diagnostic)] +#[diag(ast_passes_extern_invalid_safety)] +pub struct InvalidSafetyOnExtern { + #[primary_span] + pub item_span: Span, + #[suggestion(code = "", applicability = "maybe-incorrect")] + pub block: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bound_in_context)] pub struct BoundInContext<'a> { @@ -485,6 +494,13 @@ pub struct UnsafeItem { pub kind: &'static str, } +#[derive(Diagnostic)] +#[diag(ast_passes_missing_unsafe_on_extern)] +pub struct MissingUnsafeOnExtern { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_fieldless_union)] pub struct FieldlessUnion { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 4c29ca0ca4652..ca26b436b82ed 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1973,6 +1973,7 @@ impl<'a> State<'a> { fn print_safety(&mut self, s: ast::Safety) { match s { ast::Safety::Default => {} + ast::Safety::Safe(_) => self.word_nbsp("safe"), ast::Safety::Unsafe(_) => self.word_nbsp("unsafe"), } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 59d9b0c1a8ecd..474741fb06777 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -31,7 +31,13 @@ impl<'a> State<'a> { ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } - ast::ForeignItemKind::Static(box ast::StaticForeignItem { ty, mutability, expr }) => { + ast::ForeignItemKind::Static(box ast::StaticForeignItem { + ty, + mutability, + expr, + safety, + }) => { + self.print_safety(*safety); self.print_item_const( ident, Some(*mutability), @@ -165,7 +171,8 @@ impl<'a> State<'a> { self.print_use_tree(tree); self.word(";"); } - ast::ItemKind::Static(box StaticItem { ty, mutability: mutbl, expr: body }) => { + ast::ItemKind::Static(box StaticItem { ty, safety, mutability: mutbl, expr: body }) => { + self.print_safety(*safety); self.print_item_const( item.ident, Some(*mutbl), diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 8d0b267e1a9cc..3066e0933d94d 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -102,7 +102,7 @@ fn intern_as_new_static<'tcx>( let feed = tcx.create_def( static_id, sym::nested, - DefKind::Static { mutability: alloc.0.mutability, nested: true }, + DefKind::Static { safety: hir::Safety::Safe, mutability: alloc.0.mutability, nested: true }, ); tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index e35ce9ef28d61..3407c7b8c7925 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -711,7 +711,9 @@ fn mutability<'tcx>(ecx: &InterpCx<'tcx, impl Machine<'tcx>>, alloc_id: AllocId) // We're not using `try_global_alloc` since dangling pointers have already been handled. match ecx.tcx.global_alloc(alloc_id) { GlobalAlloc::Static(did) => { - let DefKind::Static { mutability, nested } = ecx.tcx.def_kind(did) else { bug!() }; + let DefKind::Static { safety: _, mutability, nested } = ecx.tcx.def_kind(did) else { + bug!() + }; if nested { assert!( ecx.memory.alloc_map.get(alloc_id).is_none(), diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 1b6e191c2eb09..b3d41908260ed 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -631,7 +631,10 @@ impl<'a> ExtCtxt<'a> { span, name, AttrVec::new(), - ast::ItemKind::Static(ast::StaticItem { ty, mutability, expr: Some(expr) }.into()), + ast::ItemKind::Static( + ast::StaticItem { ty, safety: ast::Safety::Default, mutability, expr: Some(expr) } + .into(), + ), ) } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index aaf9ed3de3f10..8add2b9276142 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -620,6 +620,8 @@ declare_features! ( (unstable, type_changing_struct_update, "1.58.0", Some(86555)), /// Allows unnamed fields of struct and union type (incomplete, unnamed_fields, "1.74.0", Some(49804)), + /// Allows unsafe on extern declarations and safety qualifiers over internal items. + (unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)), /// Allows unsized fn parameters. (unstable, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 649a08b6972fb..b1854923247f8 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -76,6 +76,8 @@ pub enum DefKind { /// Constant generic parameter: `struct Foo { ... }` ConstParam, Static { + /// Whether it's a `unsafe static`, `safe static` (inside extern only) or just a `static`. + safety: hir::Safety, /// Whether it's a `static mut` or just a `static`. mutability: ast::Mutability, /// Whether it's an anonymous static generated for nested allocations. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e971d0e3c1435..770dfcb98c917 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3475,9 +3475,9 @@ impl ForeignItem<'_> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum ForeignItemKind<'hir> { /// A foreign function. - Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>), + Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>, Safety), /// A foreign static item (`static ext: u8`). - Static(&'hir Ty<'hir>, Mutability), + Static(&'hir Ty<'hir>, Mutability, Safety), /// A foreign type. Type, } @@ -3545,7 +3545,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), OwnerNode::ForeignItem(ForeignItem { - kind: ForeignItemKind::Fn(fn_decl, _, _), + kind: ForeignItemKind::Fn(fn_decl, _, _, _), .. }) => Some(fn_decl), _ => None, @@ -3728,9 +3728,9 @@ impl<'hir> Node<'hir> { | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. }) - | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { - Some(fn_decl) - } + | Node::ForeignItem(ForeignItem { + kind: ForeignItemKind::Fn(fn_decl, _, _, _), .. + }) => Some(fn_decl), _ => None, } } @@ -3813,7 +3813,7 @@ impl<'hir> Node<'hir> { pub fn generics(self) -> Option<&'hir Generics<'hir>> { match self { Node::ForeignItem(ForeignItem { - kind: ForeignItemKind::Fn(_, _, generics), .. + kind: ForeignItemKind::Fn(_, _, generics, _), .. }) | Node::TraitItem(TraitItem { generics, .. }) | Node::ImplItem(ImplItem { generics, .. }) => Some(generics), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9bc2bbe0c6471..e37473df956d0 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -608,12 +608,14 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_ident(foreign_item.ident)); match foreign_item.kind { - ForeignItemKind::Fn(ref function_declaration, param_names, ref generics) => { + ForeignItemKind::Fn(ref function_declaration, param_names, ref generics, _) => { try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_fn_decl(function_declaration)); walk_list!(visitor, visit_ident, param_names.iter().copied()); } - ForeignItemKind::Static(ref typ, _) => try_visit!(visitor.visit_ty(typ)), + ForeignItemKind::Static(ref typ, _, _) => { + try_visit!(visitor.visit_ty(typ)); + } ForeignItemKind::Type => (), } V::Result::output() diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 76b6cbd6e5310..8a0623ef93e37 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -801,7 +801,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { let item = tcx.hir().foreign_item(item.id); match &item.kind { - hir::ForeignItemKind::Fn(fn_decl, _, _) => { + hir::ForeignItemKind::Fn(fn_decl, _, _, _) => { require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span); } hir::ForeignItemKind::Static(..) => { diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs index a49626eed35af..2cdcc06f53c07 100644 --- a/compiler/rustc_hir_analysis/src/check/errs.rs +++ b/compiler/rustc_hir_analysis/src/check/errs.rs @@ -41,7 +41,8 @@ fn path_if_static_mut(tcx: TyCtxt<'_>, expr: &hir::Expr<'_>) -> Option { if let hir::ExprKind::Path(qpath) = expr.kind && let hir::QPath::Resolved(_, path) = qpath && let hir::def::Res::Def(def_kind, _) = path.res - && let hir::def::DefKind::Static { mutability: Mutability::Mut, nested: false } = def_kind + && let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } = + def_kind { return Some(qpath_to_string(&tcx, &qpath)); } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index f7989aeab41bb..13180fa2673ba 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -29,7 +29,7 @@ fn equate_intrinsic_type<'tcx>( let (own_counts, span) = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) | hir::Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(.., generics), + kind: hir::ForeignItemKind::Fn(.., generics, _), .. }) => { let own_counts = tcx.generics_of(def_id).own_counts(); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 092d2d304c38e..c6e8759327f03 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1406,9 +1406,11 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn icx.lowerer().lower_fn_ty(hir_id, header.safety, header.abi, decl, Some(generics), None) } - ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { + ForeignItem(&hir::ForeignItem { + kind: ForeignItemKind::Fn(fn_decl, _, _, safety), .. + }) => { let abi = tcx.hir().get_foreign_abi(hir_id); - compute_sig_of_foreign_fn_decl(tcx, def_id, fn_decl, abi) + compute_sig_of_foreign_fn_decl(tcx, def_id, fn_decl, abi, safety) } Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => { @@ -1780,11 +1782,12 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( def_id: LocalDefId, decl: &'tcx hir::FnDecl<'tcx>, abi: abi::Abi, + safety: hir::Safety, ) -> ty::PolyFnSig<'tcx> { let safety = if abi == abi::Abi::RustIntrinsic { intrinsic_operation_unsafety(tcx, def_id) } else { - hir::Safety::Unsafe + safety }; let hir_id = tcx.local_def_id_to_hir_id(def_id); let fty = diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index c1850f78f2fb2..abc3bb838db3e 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -603,7 +603,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { match item.kind { - hir::ForeignItemKind::Fn(_, _, generics) => { + hir::ForeignItemKind::Fn(_, _, generics, _) => { self.visit_early_late(item.hir_id(), generics, |this| { intravisit::walk_foreign_item(this, item); }) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 71b08e2937699..6811f62de07b7 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -464,7 +464,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ let args = ty::GenericArgs::identity_for_item(tcx, def_id); Ty::new_fn_def(tcx, def_id.to_def_id(), args) } - ForeignItemKind::Static(t, _) => icx.lower_ty(t), + ForeignItemKind::Static(t, _, _) => icx.lower_ty(t), ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()), }, diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 3e15fddf559f3..13993a1992b7c 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -158,7 +158,7 @@ fn diagnostic_hir_wf_check<'tcx>( }, hir::Node::Field(field) => vec![field.ty], hir::Node::ForeignItem(ForeignItem { - kind: ForeignItemKind::Static(ty, _), .. + kind: ForeignItemKind::Static(ty, _, _), .. }) => vec![*ty], hir::Node::GenericParam(hir::GenericParam { kind: hir::GenericParamKind::Type { default: Some(ty), .. }, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index a8e0b3fc0793d..6983bbcb052c2 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -345,12 +345,12 @@ impl<'a> State<'a> { self.maybe_print_comment(item.span.lo()); self.print_outer_attributes(self.attrs(item.hir_id())); match item.kind { - hir::ForeignItemKind::Fn(decl, arg_names, generics) => { + hir::ForeignItemKind::Fn(decl, arg_names, generics, safety) => { self.head(""); self.print_fn( decl, hir::FnHeader { - safety: hir::Safety::Safe, + safety, constness: hir::Constness::NotConst, abi: Abi::Rust, asyncness: hir::IsAsync::NotAsync, @@ -364,7 +364,8 @@ impl<'a> State<'a> { self.word(";"); self.end() // end the outer fn box } - hir::ForeignItemKind::Static(t, m) => { + hir::ForeignItemKind::Static(t, m, safety) => { + self.print_safety(safety); self.head("static"); if m.is_mut() { self.word_space("mut"); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 24e881e28076e..0c236a4ed11f3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -462,6 +462,9 @@ lint_metavariable_wrong_operator = meta-variable repeats with different Kleene o lint_missing_fragment_specifier = missing fragment specifier +lint_missing_unsafe_on_extern = extern blocks should be unsafe + .suggestion = needs `unsafe` before the extern keyword + lint_mixed_script_confusables = the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables .includes_note = the usage includes {$includes} diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 1947041568489..1dfbe1e938282 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -202,6 +202,9 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & }; lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag); } + BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => { + lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag); + } BuiltinLintDiag::SingleUseLifetime { param_span, use_span: Some((use_span, elide)), diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index fbcf6f95fb53a..c493a989d913a 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2730,6 +2730,13 @@ pub enum DeprecatedWhereClauseLocationSugg { }, } +#[derive(LintDiagnostic)] +#[diag(lint_missing_unsafe_on_extern)] +pub struct MissingUnsafeOnExtern { + #[suggestion(code = "unsafe ", applicability = "machine-applicable")] + pub suggestion: Span, +} + #[derive(LintDiagnostic)] #[diag(lint_single_use_lifetime)] pub struct SingleUseLifetime { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 9d3a838666aff..f3a904022e9ed 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1741,13 +1741,13 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); match it.kind { - hir::ForeignItemKind::Fn(decl, _, _) if !vis.is_internal_abi(abi) => { + hir::ForeignItemKind::Fn(decl, _, _, _) if !vis.is_internal_abi(abi) => { vis.check_foreign_fn(it.owner_id.def_id, decl); } - hir::ForeignItemKind::Static(ty, _) if !vis.is_internal_abi(abi) => { + hir::ForeignItemKind::Static(ty, _, _) if !vis.is_internal_abi(abi) => { vis.check_foreign_static(it.owner_id, ty.span); } - hir::ForeignItemKind::Fn(decl, _, _) => vis.check_fn(it.owner_id.def_id, decl), + hir::ForeignItemKind::Fn(decl, _, _, _) => vis.check_fn(it.owner_id.def_id, decl), hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a7df53b836982..d0d570db04f8d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -66,6 +66,7 @@ declare_lint_pass! { META_VARIABLE_MISUSE, MISSING_ABI, MISSING_FRAGMENT_SPECIFIER, + MISSING_UNSAFE_ON_EXTERN, MUST_NOT_SUSPEND, NAMED_ARGUMENTS_USED_POSITIONALLY, NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, @@ -4803,3 +4804,40 @@ declare_lint! { reference: "issue #27970 ", }; } + +declare_lint! { + /// The `missing_unsafe_on_extern` lint detects missing unsafe keyword on extern declarations. + /// + /// ### Example + /// + /// ```rust + /// #![feature(unsafe_extern_blocks)] + /// #![warn(missing_unsafe_on_extern)] + /// #![allow(dead_code)] + /// + /// extern "C" { + /// fn foo(_: i32); + /// } + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Declaring extern items, even without ever using them, can cause Undefined Behavior. We + /// should consider all sources of Undefined Behavior to be unsafe. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub MISSING_UNSAFE_ON_EXTERN, + Allow, + "detects missing unsafe keyword on extern declarations", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #123743 ", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 746b668803b96..a2970884af40b 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -626,6 +626,9 @@ pub enum BuiltinLintDiag { UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), + MissingUnsafeOnExtern { + suggestion: Span, + }, SingleUseLifetime { /// Span of the parameter which declares this lifetime. param_span: Span, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 23a6ceb4d3e5d..dcbddad2dbcad 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -156,10 +156,14 @@ fixed_size_enum! { ( Impl { of_trait: false } ) ( Impl { of_trait: true } ) ( Closure ) - ( Static { mutability: ast::Mutability::Not, nested: false } ) - ( Static { mutability: ast::Mutability::Mut, nested: false } ) - ( Static { mutability: ast::Mutability::Not, nested: true } ) - ( Static { mutability: ast::Mutability::Mut, nested: true } ) + ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Not, nested: false } ) + ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Not, nested: false } ) + ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Mut, nested: false } ) + ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Mut, nested: false } ) + ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Not, nested: true } ) + ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Not, nested: true } ) + ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Mut, nested: true } ) + ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Mut, nested: true } ) ( Ctor(CtorOf::Struct, CtorKind::Fn) ) ( Ctor(CtorOf::Struct, CtorKind::Const) ) ( Ctor(CtorOf::Variant, CtorKind::Fn) ) diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 639c98155e705..0b3423fc1bc0f 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -305,7 +305,9 @@ impl<'hir> Map<'hir> { DefKind::InlineConst => BodyOwnerKind::Const { inline: true }, DefKind::Ctor(..) | DefKind::Fn | DefKind::AssocFn => BodyOwnerKind::Fn, DefKind::Closure => BodyOwnerKind::Closure, - DefKind::Static { mutability, nested: false } => BodyOwnerKind::Static(mutability), + DefKind::Static { safety: _, mutability, nested: false } => { + BodyOwnerKind::Static(mutability) + } dk => bug!("{:?} is not a body node: {:?}", def_id, dk), } } @@ -886,7 +888,7 @@ impl<'hir> Map<'hir> { Node::Variant(variant) => named_span(variant.span, variant.ident, None), Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)), Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(decl, _, _) => until_within(item.span, decl.output.span()), + ForeignItemKind::Fn(decl, _, _, _) => until_within(item.span, decl.output.span()), _ => named_span(item.span, item.ident, None), }, Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)), diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index b0c14cdfec938..57c8ba96a20a7 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -201,7 +201,7 @@ pub fn provide(providers: &mut Providers) { .. }) | Node::ForeignItem(&ForeignItem { - kind: ForeignItemKind::Fn(_, idents, _), + kind: ForeignItemKind::Fn(_, idents, _, _), .. }) = tcx.hir_node(tcx.local_def_id_to_hir_id(def_id)) { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index c91b67297ee80..b64b7e2b1dc6f 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -559,10 +559,10 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io: match (kind, body.source.promoted) { (_, Some(_)) => write!(w, "const ")?, // promoteds are the closest to consts (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, - (DefKind::Static { mutability: hir::Mutability::Not, nested: false }, _) => { + (DefKind::Static { safety: _, mutability: hir::Mutability::Not, nested: false }, _) => { write!(w, "static ")? } - (DefKind::Static { mutability: hir::Mutability::Mut, nested: false }, _) => { + (DefKind::Static { safety: _, mutability: hir::Mutability::Mut, nested: false }, _) => { write!(w, "static mut ")? } (_, _) if is_function => write!(w, "fn ")?, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 9a19445e3779b..d781fb1c297d9 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -2,6 +2,7 @@ use crate::build::ExprCategory; use crate::errors::*; use rustc_errors::DiagArgValue; +use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; @@ -469,7 +470,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { if self.tcx.is_mutable_static(def_id) { self.requires_unsafe(expr.span, UseOfMutableStatic); } else if self.tcx.is_foreign_item(def_id) { - self.requires_unsafe(expr.span, UseOfExternStatic); + match self.tcx.def_kind(def_id) { + DefKind::Static { safety: hir::Safety::Safe, .. } => {} + _ => self.requires_unsafe(expr.span, UseOfExternStatic), + } } } else if self.thir[arg].ty.is_unsafe_ptr() { self.requires_unsafe(expr.span, DerefOfRawPointer); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 53757c38e8b04..37c99958fc88b 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -226,10 +226,11 @@ impl<'a> Parser<'a> { self.expect_keyword(kw::Extern)?; self.parse_item_foreign_mod(attrs, safety)? } else if self.is_static_global() { + let safety = self.parse_safety(Case::Sensitive); // STATIC ITEM self.bump(); // `static` let mutability = self.parse_mutability(); - let (ident, item) = self.parse_static_item(mutability)?; + let (ident, item) = self.parse_static_item(safety, mutability)?; (ident, ItemKind::Static(Box::new(item))) } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM @@ -952,7 +953,7 @@ impl<'a> Parser<'a> { let kind = match AssocItemKind::try_from(kind) { Ok(kind) => kind, Err(kind) => match kind { - ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => { + ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => { self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { defaultness: Defaultness::Final, @@ -1221,6 +1222,7 @@ impl<'a> Parser<'a> { ty, mutability: Mutability::Not, expr, + safety: Safety::Default, })) } _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), @@ -1258,7 +1260,10 @@ impl<'a> Parser<'a> { matches!(token.kind, token::BinOp(token::Or) | token::OrOr) }) } else { - false + let quals: &[Symbol] = &[kw::Unsafe, kw::Safe]; + // `$qual static` + quals.iter().any(|&kw| self.check_keyword(kw)) + && self.look_ahead(1, |t| t.is_keyword(kw::Static)) } } @@ -1319,7 +1324,11 @@ impl<'a> Parser<'a> { /// ```ebnf /// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ; /// ``` - fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> { + fn parse_static_item( + &mut self, + safety: Safety, + mutability: Mutability, + ) -> PResult<'a, (Ident, StaticItem)> { let ident = self.parse_ident()?; if self.token.kind == TokenKind::Lt && self.may_recover() { @@ -1340,7 +1349,7 @@ impl<'a> Parser<'a> { self.expect_semi()?; - Ok((ident, StaticItem { ty, mutability, expr })) + Ok((ident, StaticItem { ty, safety, mutability, expr })) } /// Parse a constant item with the prefix `"const"` already parsed. @@ -2400,9 +2409,9 @@ impl<'a> Parser<'a> { // `pub` is added in case users got confused with the ordering like `async pub fn`, // only if it wasn't preceded by `default` as `default pub` is invalid. let quals: &[Symbol] = if check_pub { - &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern] + &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] } else { - &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern] + &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] }; self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: @@ -2537,11 +2546,27 @@ impl<'a> Parser<'a> { } else if self.check_keyword(kw::Unsafe) { match safety { Safety::Unsafe(sp) => Some(WrongKw::Duplicated(sp)), + Safety::Safe(sp) => { + recover_safety = Safety::Unsafe(self.token.span); + Some(WrongKw::Misplaced(sp)) + } Safety::Default => { recover_safety = Safety::Unsafe(self.token.span); Some(WrongKw::Misplaced(ext_start_sp)) } } + } else if self.check_keyword(kw::Safe) { + match safety { + Safety::Safe(sp) => Some(WrongKw::Duplicated(sp)), + Safety::Unsafe(sp) => { + recover_safety = Safety::Safe(self.token.span); + Some(WrongKw::Misplaced(sp)) + } + Safety::Default => { + recover_safety = Safety::Safe(self.token.span); + Some(WrongKw::Misplaced(ext_start_sp)) + } + } } else { None }; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index bab8b6c06ebee..8f733b4fcbbcd 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1221,6 +1221,8 @@ impl<'a> Parser<'a> { fn parse_safety(&mut self, case: Case) -> Safety { if self.eat_keyword_case(kw::Unsafe, case) { Safety::Unsafe(self.prev_token.uninterpolated_span()) + } else if self.eat_keyword_case(kw::Safe, case) { + Safety::Safe(self.prev_token.uninterpolated_span()) } else { Safety::Default } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index cad10571afe62..d7416ead325dd 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -2,6 +2,7 @@ use crate::{ImplTraitContext, Resolver}; use rustc_ast::visit::FnKind; use rustc_ast::*; use rustc_expand::expand::AstFragment; +use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::LocalDefId; use rustc_span::hygiene::LocalExpnId; @@ -128,7 +129,11 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { ItemKind::Union(..) => DefKind::Union, ItemKind::ExternCrate(..) => DefKind::ExternCrate, ItemKind::TyAlias(..) => DefKind::TyAlias, - ItemKind::Static(s) => DefKind::Static { mutability: s.mutability, nested: false }, + ItemKind::Static(s) => DefKind::Static { + safety: hir::Safety::Safe, + mutability: s.mutability, + nested: false, + }, ItemKind::Const(..) => DefKind::Const, ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn, ItemKind::MacroDef(..) => { @@ -211,8 +216,18 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { let def_kind = match fi.kind { - ForeignItemKind::Static(box StaticForeignItem { ty: _, mutability, expr: _ }) => { - DefKind::Static { mutability, nested: false } + ForeignItemKind::Static(box StaticForeignItem { + ty: _, + mutability, + expr: _, + safety, + }) => { + let safety = match safety { + ast::Safety::Unsafe(_) | ast::Safety::Default => hir::Safety::Unsafe, + ast::Safety::Safe(_) => hir::Safety::Safe, + }; + + DefKind::Static { safety, mutability, nested: false } } ForeignItemKind::Fn(_) => DefKind::Fn, ForeignItemKind::TyAlias(_) => DefKind::ForeignTy, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fe883e03c44cd..f530d1dd1d4bc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -103,6 +103,7 @@ symbols! { MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", + Safe: "safe", Union: "union", Yeet: "yeet", } @@ -1964,6 +1965,7 @@ symbols! { unsafe_block_in_unsafe_fn, unsafe_cell, unsafe_cell_raw_get, + unsafe_extern_blocks, unsafe_no_drop_flag, unsafe_pin_internals, unsize, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d4e28927728f4..2dcbbf0d1509a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3075,7 +3075,9 @@ fn clean_maybe_renamed_foreign_item<'tcx>( let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { let kind = match item.kind { - hir::ForeignItemKind::Fn(decl, names, generics) => { + // FIXME(missing_unsafe_on_extern) handle safety of foreign fns. + // Safety was added as part of the implementation of unsafe extern blocks PR #124482 + hir::ForeignItemKind::Fn(decl, names, generics, _) => { let (generics, decl) = enter_impl_trait(cx, |cx| { // NOTE: generics must be cleaned before args let generics = clean_generics(generics, cx); @@ -3085,7 +3087,9 @@ fn clean_maybe_renamed_foreign_item<'tcx>( }); ForeignFunctionItem(Box::new(Function { decl, generics })) } - hir::ForeignItemKind::Static(ty, mutability) => { + // FIXME(missing_unsafe_on_extern) handle safety of foreign statics. + // Safety was added as part of the implementation of unsafe extern blocks PR #124482 + hir::ForeignItemKind::Static(ty, mutability, _) => { ForeignStaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: None }) } hir::ForeignItemKind::Type => ForeignTypeItem, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 8ab24a8c12e58..440b02a1fa75b 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -12,7 +12,7 @@ use rustc_errors::{Applicability, Diag, DiagMessage}; use rustc_hir::def::Namespace::*; use rustc_hir::def::{DefKind, Namespace, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; -use rustc_hir::Mutability; +use rustc_hir::{Mutability, Safety}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution}; @@ -1517,7 +1517,11 @@ impl Disambiguator { "union" => Kind(DefKind::Union), "module" | "mod" => Kind(DefKind::Mod), "const" | "constant" => Kind(DefKind::Const), - "static" => Kind(DefKind::Static { mutability: Mutability::Not, nested: false }), + "static" => Kind(DefKind::Static { + mutability: Mutability::Not, + nested: false, + safety: Safety::Safe, + }), "function" | "fn" | "method" => Kind(DefKind::Fn), "derive" => Kind(DefKind::Macro(MacroKind::Derive)), "type" => NS(Namespace::TypeNS), diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index bbdde3049dbd3..c70f5c2df8420 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -308,13 +308,15 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { ty: lt, mutability: lm, expr: le, + safety: ls, }), Static(box StaticItem { ty: rt, mutability: rm, expr: re, + safety: rs, }), - ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re), + ) => lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le, re), ( Const(box ConstItem { defaultness: ld, @@ -451,13 +453,15 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { ty: lt, mutability: lm, expr: le, + safety: ls, }), Static(box StaticForeignItem { ty: rt, mutability: rm, expr: re, + safety: rs, }), - ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re), + ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re) && ls == rs, ( Fn(box ast::Fn { defaultness: ld, diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 09e1dbde1d05e..fd59aedadfed5 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -111,6 +111,7 @@ pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str pub(crate) fn format_safety(unsafety: ast::Safety) -> &'static str { match unsafety { ast::Safety::Unsafe(..) => "unsafe ", + ast::Safety::Safe(..) => "safe ", ast::Safety::Default => "", } } diff --git a/tests/pretty/hir-fn-variadic.pp b/tests/pretty/hir-fn-variadic.pp index 978e65c825bb3..dfbaff696440b 100644 --- a/tests/pretty/hir-fn-variadic.pp +++ b/tests/pretty/hir-fn-variadic.pp @@ -9,7 +9,7 @@ extern crate std; extern "C" { - fn foo(x: i32, va1: ...); + unsafe fn foo(x: i32, va1: ...); } unsafe extern "C" fn bar(_: i32, mut va2: ...) -> usize { va2.arg::() } diff --git a/tests/ui/async-await/no-async-const.rs b/tests/ui/async-await/no-async-const.rs index c5485ebc9b624..38a5df3576bb0 100644 --- a/tests/ui/async-await/no-async-const.rs +++ b/tests/ui/async-await/no-async-const.rs @@ -2,5 +2,5 @@ //@ compile-flags: --crate-type lib pub async const fn x() {} -//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const` +//~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const` //~| ERROR functions cannot be both `const` and `async` diff --git a/tests/ui/async-await/no-async-const.stderr b/tests/ui/async-await/no-async-const.stderr index 524d778c09b85..d692ba8f47375 100644 --- a/tests/ui/async-await/no-async-const.stderr +++ b/tests/ui/async-await/no-async-const.stderr @@ -1,10 +1,10 @@ -error: expected one of `extern`, `fn`, or `unsafe`, found keyword `const` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const` --> $DIR/no-async-const.rs:4:11 | LL | pub async const fn x() {} | ------^^^^^ | | | - | | expected one of `extern`, `fn`, or `unsafe` + | | expected one of `extern`, `fn`, `safe`, or `unsafe` | help: `const` must come before `async`: `const async` | = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/tests/ui/coroutine/async_gen_fn.none.stderr b/tests/ui/coroutine/async_gen_fn.none.stderr index 7950251a75daa..047f4d82486d8 100644 --- a/tests/ui/coroutine/async_gen_fn.none.stderr +++ b/tests/ui/coroutine/async_gen_fn.none.stderr @@ -7,11 +7,11 @@ LL | async gen fn foo() {} = help: pass `--edition 2021` to `rustc` = note: for more on editions, read https://doc.rust-lang.org/edition-guide -error: expected one of `extern`, `fn`, or `unsafe`, found `gen` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found `gen` --> $DIR/async_gen_fn.rs:4:7 | LL | async gen fn foo() {} - | ^^^ expected one of `extern`, `fn`, or `unsafe` + | ^^^ expected one of `extern`, `fn`, `safe`, or `unsafe` error: aborting due to 2 previous errors diff --git a/tests/ui/coroutine/async_gen_fn.rs b/tests/ui/coroutine/async_gen_fn.rs index 9e96ecf3ea69f..e8be0434b9ef9 100644 --- a/tests/ui/coroutine/async_gen_fn.rs +++ b/tests/ui/coroutine/async_gen_fn.rs @@ -3,7 +3,7 @@ async gen fn foo() {} //[none]~^ ERROR: `async fn` is not permitted in Rust 2015 -//[none]~| ERROR: expected one of `extern`, `fn`, or `unsafe`, found `gen` +//[none]~| ERROR: expected one of `extern`, `fn`, `safe`, or `unsafe`, found `gen` //[e2024]~^^^ ERROR: gen blocks are experimental fn main() {} diff --git a/tests/ui/coroutine/gen_fn.none.stderr b/tests/ui/coroutine/gen_fn.none.stderr index c5342ee22e62f..590210641aed4 100644 --- a/tests/ui/coroutine/gen_fn.none.stderr +++ b/tests/ui/coroutine/gen_fn.none.stderr @@ -1,8 +1,8 @@ -error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` +error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` --> $DIR/gen_fn.rs:4:1 | LL | gen fn foo() {} - | ^^^ expected one of 9 possible tokens + | ^^^ expected one of 10 possible tokens error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs index 3228650f41524..d47b7e576d002 100644 --- a/tests/ui/coroutine/gen_fn.rs +++ b/tests/ui/coroutine/gen_fn.rs @@ -2,7 +2,7 @@ //@[e2024] compile-flags: --edition 2024 -Zunstable-options gen fn foo() {} -//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` +//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` //[e2024]~^^ ERROR: gen blocks are experimental fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs new file mode 100644 index 0000000000000..eab134a4a4de4 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs @@ -0,0 +1,5 @@ +unsafe extern "C" { + //~^ ERROR extern block cannot be declared unsafe +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr new file mode 100644 index 0000000000000..7e9b199a2db55 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr @@ -0,0 +1,8 @@ +error: extern block cannot be declared unsafe + --> $DIR/feature-gate-unsafe-extern-blocks.rs:1:1 + | +LL | unsafe extern "C" { + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/duplicate-visibility.rs b/tests/ui/parser/duplicate-visibility.rs index 54955944c7d35..f0ee60873da01 100644 --- a/tests/ui/parser/duplicate-visibility.rs +++ b/tests/ui/parser/duplicate-visibility.rs @@ -2,8 +2,8 @@ fn main() {} extern "C" { //~ NOTE while parsing this item list starting here pub pub fn foo(); - //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `unsafe`, or `use`, found keyword `pub` - //~| NOTE expected one of 8 possible tokens + //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` + //~| NOTE expected one of 9 possible tokens //~| HELP there is already a visibility modifier, remove one //~| NOTE explicit visibility first seen here } //~ NOTE the item list ends here diff --git a/tests/ui/parser/duplicate-visibility.stderr b/tests/ui/parser/duplicate-visibility.stderr index b578b1fe26e8d..0d1421ee7f4e4 100644 --- a/tests/ui/parser/duplicate-visibility.stderr +++ b/tests/ui/parser/duplicate-visibility.stderr @@ -1,4 +1,4 @@ -error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `unsafe`, or `use`, found keyword `pub` +error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` --> $DIR/duplicate-visibility.rs:4:9 | LL | extern "C" { @@ -6,7 +6,7 @@ LL | extern "C" { LL | pub pub fn foo(); | ^^^ | | - | expected one of 8 possible tokens + | expected one of 9 possible tokens | help: there is already a visibility modifier, remove one ... LL | } diff --git a/tests/ui/parser/fn-header-semantic-fail.rs b/tests/ui/parser/fn-header-semantic-fail.rs index 6ed173b6854db..5907ac0526044 100644 --- a/tests/ui/parser/fn-header-semantic-fail.rs +++ b/tests/ui/parser/fn-header-semantic-fail.rs @@ -44,13 +44,14 @@ fn main() { extern "C" { async fn fe1(); //~ ERROR functions in `extern` blocks cannot have qualifiers - unsafe fn fe2(); //~ ERROR functions in `extern` blocks cannot have qualifiers + unsafe fn fe2(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers - const async unsafe extern "C" fn fe5(); //~ ERROR functions in `extern` blocks - //~| ERROR functions in `extern` blocks + const async unsafe extern "C" fn fe5(); + //~^ ERROR functions in `extern` blocks //~| ERROR functions in `extern` blocks //~| ERROR functions in `extern` blocks //~| ERROR functions cannot be both `const` and `async` + //~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers } } diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr index cfc54839eb752..abaa6527b0aac 100644 --- a/tests/ui/parser/fn-header-semantic-fail.stderr +++ b/tests/ui/parser/fn-header-semantic-fail.stderr @@ -78,14 +78,14 @@ LL | extern "C" { LL | async fn fe1(); | ^^^^^ help: remove this qualifier -error: functions in `extern` blocks cannot have qualifiers +error: items in unadorned `extern` blocks cannot have safety qualifiers --> $DIR/fn-header-semantic-fail.rs:47:9 | LL | extern "C" { - | ---------- in this `extern` block + | ---------- help: add unsafe to this `extern` block LL | async fn fe1(); LL | unsafe fn fe2(); - | ^^^^^^ help: remove this qualifier + | ^^^^^^^^^^^^^^^^ error: functions in `extern` blocks cannot have qualifiers --> $DIR/fn-header-semantic-fail.rs:48:9 @@ -105,14 +105,14 @@ LL | extern "C" { LL | extern "C" fn fe4(); | ^^^^^^^^^^ help: remove this qualifier -error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:50:21 +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/fn-header-semantic-fail.rs:50:9 | LL | extern "C" { - | ---------- in this `extern` block + | ---------- help: add unsafe to this `extern` block ... LL | const async unsafe extern "C" fn fe5(); - | ^^^^^^ help: remove this qualifier + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: functions in `extern` blocks cannot have qualifiers --> $DIR/fn-header-semantic-fail.rs:50:15 diff --git a/tests/ui/parser/issues/issue-76437-async.rs b/tests/ui/parser/issues/issue-76437-async.rs index 497e269d634e5..3fafaad0277c5 100644 --- a/tests/ui/parser/issues/issue-76437-async.rs +++ b/tests/ui/parser/issues/issue-76437-async.rs @@ -2,6 +2,6 @@ mod t { async pub fn t() {} - //~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` + //~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` //~| HELP visibility `pub` must come before `async` } diff --git a/tests/ui/parser/issues/issue-76437-async.stderr b/tests/ui/parser/issues/issue-76437-async.stderr index 7f2df5c873643..483599135f566 100644 --- a/tests/ui/parser/issues/issue-76437-async.stderr +++ b/tests/ui/parser/issues/issue-76437-async.stderr @@ -1,10 +1,10 @@ -error: expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-76437-async.rs:4:11 | LL | async pub fn t() {} | ------^^^ | | | - | | expected one of `extern`, `fn`, or `unsafe` + | | expected one of `extern`, `fn`, `safe`, or `unsafe` | help: visibility `pub` must come before `async`: `pub async` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-76437-const-async.rs b/tests/ui/parser/issues/issue-76437-const-async.rs index 45d53c6393321..d8eb6cdecf1b1 100644 --- a/tests/ui/parser/issues/issue-76437-const-async.rs +++ b/tests/ui/parser/issues/issue-76437-const-async.rs @@ -2,6 +2,6 @@ mod t { const async pub fn t() {} - //~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` + //~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` //~| HELP visibility `pub` must come before `const async` } diff --git a/tests/ui/parser/issues/issue-76437-const-async.stderr b/tests/ui/parser/issues/issue-76437-const-async.stderr index a9acccdce1824..81fa8a5f557e0 100644 --- a/tests/ui/parser/issues/issue-76437-const-async.stderr +++ b/tests/ui/parser/issues/issue-76437-const-async.stderr @@ -1,10 +1,10 @@ -error: expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-76437-const-async.rs:4:17 | LL | const async pub fn t() {} | ------------^^^ | | | - | | expected one of `extern`, `fn`, or `unsafe` + | | expected one of `extern`, `fn`, `safe`, or `unsafe` | help: visibility `pub` must come before `const async`: `pub const async` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-76437-const.rs b/tests/ui/parser/issues/issue-76437-const.rs index c3431e3567bf3..dad63f137c0f4 100644 --- a/tests/ui/parser/issues/issue-76437-const.rs +++ b/tests/ui/parser/issues/issue-76437-const.rs @@ -2,6 +2,6 @@ mod t { const pub fn t() {} - //~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` + //~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` //~| HELP visibility `pub` must come before `const` } diff --git a/tests/ui/parser/issues/issue-76437-const.stderr b/tests/ui/parser/issues/issue-76437-const.stderr index 4c36d773d60e6..005a27b7c2498 100644 --- a/tests/ui/parser/issues/issue-76437-const.stderr +++ b/tests/ui/parser/issues/issue-76437-const.stderr @@ -1,10 +1,10 @@ -error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-76437-const.rs:4:11 | LL | const pub fn t() {} | ------^^^ | | | - | | expected one of `async`, `extern`, `fn`, or `unsafe` + | | expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` | help: visibility `pub` must come before `const`: `pub const` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-86895.rs b/tests/ui/parser/issues/issue-86895.rs index 4cd09843107dd..3e5dc41e2f4d0 100644 --- a/tests/ui/parser/issues/issue-86895.rs +++ b/tests/ui/parser/issues/issue-86895.rs @@ -1,3 +1,3 @@ const pub () {} -//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe` +//~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` pub fn main() {} diff --git a/tests/ui/parser/issues/issue-86895.stderr b/tests/ui/parser/issues/issue-86895.stderr index dcde7242d3987..14183ee0a5cf5 100644 --- a/tests/ui/parser/issues/issue-86895.stderr +++ b/tests/ui/parser/issues/issue-86895.stderr @@ -1,8 +1,8 @@ -error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-86895.rs:1:7 | LL | const pub () {} - | ^^^ expected one of `async`, `extern`, `fn`, or `unsafe` + | ^^^ expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.rs b/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.rs index 694729376ba88..e6235b1e8923f 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.rs +++ b/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.rs @@ -3,8 +3,8 @@ // Test that even when `const` is already present, the proposed fix is to remove the second `const` const async const fn test() {} -//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const` -//~| NOTE expected one of `extern`, `fn`, or `unsafe` +//~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const` +//~| NOTE expected one of `extern`, `fn`, `safe`, or `unsafe` //~| HELP `const` already used earlier, remove this one //~| NOTE `const` first seen here //~| ERROR functions cannot be both `const` and `async` diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.stderr index 4c55179ce2379..ed2e4d8154929 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.stderr +++ b/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.stderr @@ -1,10 +1,10 @@ -error: expected one of `extern`, `fn`, or `unsafe`, found keyword `const` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const` --> $DIR/const-async-const.rs:5:13 | LL | const async const fn test() {} | ^^^^^ | | - | expected one of `extern`, `fn`, or `unsafe` + | expected one of `extern`, `fn`, `safe`, or `unsafe` | help: `const` already used earlier, remove this one | note: `const` first seen here diff --git a/tests/ui/parser/issues/issue-87694-duplicated-pub.rs b/tests/ui/parser/issues/issue-87694-duplicated-pub.rs index e3ea61dc4ada6..816c8ff2a9f5f 100644 --- a/tests/ui/parser/issues/issue-87694-duplicated-pub.rs +++ b/tests/ui/parser/issues/issue-87694-duplicated-pub.rs @@ -1,5 +1,5 @@ pub const pub fn test() {} -//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` -//~| NOTE expected one of `async`, `extern`, `fn`, or `unsafe` +//~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` +//~| NOTE expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` //~| HELP there is already a visibility modifier, remove one //~| NOTE explicit visibility first seen here diff --git a/tests/ui/parser/issues/issue-87694-duplicated-pub.stderr b/tests/ui/parser/issues/issue-87694-duplicated-pub.stderr index a210238652abb..dd75f32f68ff2 100644 --- a/tests/ui/parser/issues/issue-87694-duplicated-pub.stderr +++ b/tests/ui/parser/issues/issue-87694-duplicated-pub.stderr @@ -1,10 +1,10 @@ -error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-87694-duplicated-pub.rs:1:11 | LL | pub const pub fn test() {} | ^^^ | | - | expected one of `async`, `extern`, `fn`, or `unsafe` + | expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` | help: there is already a visibility modifier, remove one | note: explicit visibility first seen here diff --git a/tests/ui/parser/issues/issue-87694-misplaced-pub.rs b/tests/ui/parser/issues/issue-87694-misplaced-pub.rs index 3f824617cade1..b5b0bc5b2fc9e 100644 --- a/tests/ui/parser/issues/issue-87694-misplaced-pub.rs +++ b/tests/ui/parser/issues/issue-87694-misplaced-pub.rs @@ -1,5 +1,5 @@ const pub fn test() {} -//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` -//~| NOTE expected one of `async`, `extern`, `fn`, or `unsafe` +//~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` +//~| NOTE expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` //~| HELP visibility `pub` must come before `const` //~| SUGGESTION pub const diff --git a/tests/ui/parser/issues/issue-87694-misplaced-pub.stderr b/tests/ui/parser/issues/issue-87694-misplaced-pub.stderr index 6f686a7e50437..d35e09dceaf7f 100644 --- a/tests/ui/parser/issues/issue-87694-misplaced-pub.stderr +++ b/tests/ui/parser/issues/issue-87694-misplaced-pub.stderr @@ -1,10 +1,10 @@ -error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-87694-misplaced-pub.rs:1:7 | LL | const pub fn test() {} | ------^^^ | | | - | | expected one of `async`, `extern`, `fn`, or `unsafe` + | | expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` | help: visibility `pub` must come before `const`: `pub const` error: aborting due to 1 previous error diff --git a/tests/ui/parser/no-const-fn-in-extern-block.rs b/tests/ui/parser/no-const-fn-in-extern-block.rs index d6c578681ccc7..3ad9ba006d3da 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.rs +++ b/tests/ui/parser/no-const-fn-in-extern-block.rs @@ -3,7 +3,7 @@ extern "C" { //~^ ERROR functions in `extern` blocks cannot have qualifiers const unsafe fn bar(); //~^ ERROR functions in `extern` blocks cannot have qualifiers - //~| ERROR functions in `extern` blocks cannot have qualifiers + //~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers } fn main() {} 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 948ce669112c3..892024ce89340 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.stderr +++ b/tests/ui/parser/no-const-fn-in-extern-block.stderr @@ -6,14 +6,14 @@ 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:11 +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/no-const-fn-in-extern-block.rs:4:5 | LL | extern "C" { - | ---------- in this `extern` block + | ---------- help: add unsafe to this `extern` block ... LL | const unsafe fn bar(); - | ^^^^^^ help: remove this qualifier + | ^^^^^^^^^^^^^^^^^^^^^^ error: functions in `extern` blocks cannot have qualifiers --> $DIR/no-const-fn-in-extern-block.rs:4:5 diff --git a/tests/ui/parser/unsafe-foreign-mod-2.rs b/tests/ui/parser/unsafe-foreign-mod-2.rs index 77856fb67340e..0b63a993c5b9e 100644 --- a/tests/ui/parser/unsafe-foreign-mod-2.rs +++ b/tests/ui/parser/unsafe-foreign-mod-2.rs @@ -1,8 +1,8 @@ extern "C" unsafe { - //~^ ERROR expected `{`, found keyword `unsafe` - //~| ERROR extern block cannot be declared unsafe + //~^ ERROR expected `{`, found keyword `unsafe` + //~| ERROR extern block cannot be declared unsafe unsafe fn foo(); - //~^ ERROR functions in `extern` blocks cannot have qualifiers + //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers } fn main() {} diff --git a/tests/ui/parser/unsafe-foreign-mod-2.stderr b/tests/ui/parser/unsafe-foreign-mod-2.stderr index fc05184f018f5..e59352395ed6e 100644 --- a/tests/ui/parser/unsafe-foreign-mod-2.stderr +++ b/tests/ui/parser/unsafe-foreign-mod-2.stderr @@ -10,14 +10,14 @@ error: extern block cannot be declared unsafe LL | extern "C" unsafe { | ^^^^^^ -error: functions in `extern` blocks cannot have qualifiers +error: items in unadorned `extern` blocks cannot have safety qualifiers --> $DIR/unsafe-foreign-mod-2.rs:4:5 | LL | extern "C" unsafe { - | ----------------- in this `extern` block + | ----------------- help: add unsafe to this `extern` block ... LL | unsafe fn foo(); - | ^^^^^^ help: remove this qualifier + | ^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr new file mode 100644 index 0000000000000..3a99caa719b53 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe function or block + --> $DIR/extern-items-unsafe.rs:14:5 + | +LL | test1(TEST1); + | ^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/extern-items-unsafe.rs:14:11 + | +LL | test1(TEST1); + | ^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr new file mode 100644 index 0000000000000..fcf937b7ac577 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe block + --> $DIR/extern-items-unsafe.rs:14:5 + | +LL | test1(TEST1); + | ^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe block + --> $DIR/extern-items-unsafe.rs:14:11 + | +LL | test1(TEST1); + | ^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs new file mode 100644 index 0000000000000..ad569a256db90 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs @@ -0,0 +1,25 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_extern_blocks)] + +unsafe extern "C" { + static TEST1: i32; + fn test1(i: i32); +} + +fn test2() { + test1(TEST1); + //~^ ERROR: call to unsafe function `test1` is unsafe + //~| ERROR: use of extern static is unsafe +} + +fn test3() { + unsafe { + test1(TEST1); + } +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr new file mode 100644 index 0000000000000..d456cfc6829e1 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr @@ -0,0 +1,12 @@ +error: extern blocks must be unsafe + --> $DIR/extern-items.rs:9:1 + | +LL | / extern "C" { +LL | | +LL | | static TEST1: i32; +LL | | fn test1(i: i32); +LL | | } + | |_^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs new file mode 100644 index 0000000000000..16fa1bbb8a404 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs @@ -0,0 +1,20 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2021] check-pass +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_extern_blocks)] + +extern "C" { + //[edition2024]~^ ERROR extern blocks must be unsafe + static TEST1: i32; + fn test1(i: i32); +} + +unsafe extern "C" { + static TEST2: i32; + fn test2(i: i32); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs new file mode 100644 index 0000000000000..74cd5621fce93 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs @@ -0,0 +1,18 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options +//@ check-pass + +#![feature(unsafe_extern_blocks)] + +unsafe extern "C" { + safe static TEST1: i32; + safe fn test1(i: i32); +} + +fn test2() { + test1(TEST1); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr new file mode 100644 index 0000000000000..411cf48b4866a --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr @@ -0,0 +1,20 @@ +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +LL | +LL | safe static TEST1: i32; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:12:5 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +... +LL | safe fn test1(i: i32); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr new file mode 100644 index 0000000000000..b634adc299960 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr @@ -0,0 +1,32 @@ +error: extern blocks must be unsafe + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:1 + | +LL | / extern "C" { +LL | | +LL | | safe static TEST1: i32; +LL | | +LL | | safe fn test1(i: i32); +LL | | +LL | | } + | |_^ + +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +LL | +LL | safe static TEST1: i32; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:12:5 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +... +LL | safe fn test1(i: i32); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs new file mode 100644 index 0000000000000..11f55cb195f29 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs @@ -0,0 +1,20 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_extern_blocks)] + +extern "C" { + //[edition2024]~^ ERROR extern blocks must be unsafe + safe static TEST1: i32; + //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers + safe fn test1(i: i32); + //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers +} + +fn test2() { + test1(TEST1); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed new file mode 100644 index 0000000000000..10c19759d8aaa --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed @@ -0,0 +1,19 @@ +//@ run-rustfix + +#![feature(unsafe_extern_blocks)] +#![deny(missing_unsafe_on_extern)] +#![allow(unused)] + +unsafe extern "C" { + //~^ ERROR extern blocks should be unsafe [missing_unsafe_on_extern] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + static TEST1: i32; + fn test1(i: i32); +} + +unsafe extern "C" { + static TEST2: i32; + fn test2(i: i32); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs new file mode 100644 index 0000000000000..b81e52ddc5843 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs @@ -0,0 +1,19 @@ +//@ run-rustfix + +#![feature(unsafe_extern_blocks)] +#![deny(missing_unsafe_on_extern)] +#![allow(unused)] + +extern "C" { + //~^ ERROR extern blocks should be unsafe [missing_unsafe_on_extern] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + static TEST1: i32; + fn test1(i: i32); +} + +unsafe extern "C" { + static TEST2: i32; + fn test2(i: i32); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr new file mode 100644 index 0000000000000..0a3c2cd25e3fe --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr @@ -0,0 +1,25 @@ +error: extern blocks should be unsafe + --> $DIR/unsafe-extern-suggestion.rs:7:1 + | +LL | extern "C" { + | ^ + | | + | _help: needs `unsafe` before the extern keyword: `unsafe` + | | +LL | | +LL | | +LL | | static TEST1: i32; +LL | | fn test1(i: i32); +LL | | } + | |_^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123743 +note: the lint level is defined here + --> $DIR/unsafe-extern-suggestion.rs:4:9 + | +LL | #![deny(missing_unsafe_on_extern)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr new file mode 100644 index 0000000000000..8bb7ffefeea9e --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe function or block + --> $DIR/unsafe-items.rs:20:5 + | +LL | test1(TEST1); + | ^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/unsafe-items.rs:20:11 + | +LL | test1(TEST1); + | ^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr new file mode 100644 index 0000000000000..9a30142a632c5 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe block + --> $DIR/unsafe-items.rs:20:5 + | +LL | test1(TEST1); + | ^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe block + --> $DIR/unsafe-items.rs:20:11 + | +LL | test1(TEST1); + | ^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs new file mode 100644 index 0000000000000..9066953abc615 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs @@ -0,0 +1,25 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_extern_blocks)] + +unsafe extern "C" { + unsafe static TEST1: i32; + unsafe fn test1(i: i32); +} + +fn test2() { + unsafe { + test1(TEST1); + } +} + +fn test3() { + test1(TEST1); + //~^ ERROR: call to unsafe function `test1` is unsafe + //~| ERROR: use of extern static is unsafe +} + +fn main() {} diff --git a/tests/ui/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/expanded-exhaustive.rs index 6aa032d7ed83e..92c2e7b488478 100644 --- a/tests/ui/unpretty/expanded-exhaustive.rs +++ b/tests/ui/unpretty/expanded-exhaustive.rs @@ -25,6 +25,7 @@ #![feature(trait_alias)] #![feature(try_blocks)] #![feature(unnamed_fields)] +#![feature(unsafe_extern_blocks)] #![feature(yeet_expr)] #![allow(incomplete_features)] @@ -473,8 +474,8 @@ mod items { /// ItemKind::ForeignMod mod item_foreign_mod { - extern "C++" {} - extern {} + unsafe extern "C++" {} + unsafe extern {} } /// ItemKind::GlobalAsm diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout index 8737063bf3cc7..9e45f57af354d 100644 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ b/tests/ui/unpretty/expanded-exhaustive.stdout @@ -26,6 +26,7 @@ #![feature(trait_alias)] #![feature(try_blocks)] #![feature(unnamed_fields)] +#![feature(unsafe_extern_blocks)] #![feature(yeet_expr)] #![allow(incomplete_features)] #[prelude_import] @@ -451,8 +452,8 @@ mod items { mod item_mod { } /// ItemKind::ForeignMod mod item_foreign_mod { - extern "C++" {} - extern {} + unsafe extern "C++" {} + unsafe extern {} } /// ItemKind::GlobalAsm mod item_global_asm {