Handle safety keyword for extern block inner items

This commit is contained in:
Santiago Pastorino 2024-05-23 10:01:05 -03:00
parent bbddc9b58f
commit 2a377122dd
No known key found for this signature in database
GPG Key ID: 8131A24E0C79EFAF
52 changed files with 168 additions and 84 deletions

View File

@ -2501,6 +2501,8 @@ pub enum IsAuto {
pub enum Safety { pub enum Safety {
/// `unsafe` an item is explicitly marked as `unsafe`. /// `unsafe` an item is explicitly marked as `unsafe`.
Unsafe(Span), 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 /// Default means no value was provided, it will take a default value given the context in
/// which is used. /// which is used.
Default, Default,
@ -3171,6 +3173,7 @@ pub struct StaticItem {
#[derive(Clone, Encodable, Decodable, Debug)] #[derive(Clone, Encodable, Decodable, Debug)]
pub struct StaticForeignItem { pub struct StaticForeignItem {
pub ty: P<Ty>, pub ty: P<Ty>,
pub safety: Safety,
pub mutability: Mutability, pub mutability: Mutability,
pub expr: Option<P<Expr>>, pub expr: Option<P<Expr>>,
} }
@ -3179,6 +3182,7 @@ impl From<StaticItem> for StaticForeignItem {
fn from(static_item: StaticItem) -> StaticForeignItem { fn from(static_item: StaticItem) -> StaticForeignItem {
StaticForeignItem { StaticForeignItem {
ty: static_item.ty, ty: static_item.ty,
safety: Safety::Default,
mutability: static_item.mutability, mutability: static_item.mutability,
expr: static_item.expr, expr: static_item.expr,
} }

View File

@ -862,6 +862,7 @@ fn visit_defaultness<T: MutVisitor>(defaultness: &mut Defaultness, vis: &mut T)
fn visit_safety<T: MutVisitor>(safety: &mut Safety, vis: &mut T) { fn visit_safety<T: MutVisitor>(safety: &mut Safety, vis: &mut T) {
match safety { match safety {
Safety::Unsafe(span) => vis.visit_span(span), Safety::Unsafe(span) => vis.visit_span(span),
Safety::Safe(span) => vis.visit_span(span),
Safety::Default => {} Safety::Default => {}
} }
} }
@ -1289,7 +1290,12 @@ pub fn noop_flat_map_item<K: NoopVisitItemKind>(
impl NoopVisitItemKind for ForeignItemKind { impl NoopVisitItemKind for ForeignItemKind {
fn noop_visit(&mut self, visitor: &mut impl MutVisitor) { fn noop_visit(&mut self, visitor: &mut impl MutVisitor) {
match self { match self {
ForeignItemKind::Static(box StaticForeignItem { ty, mutability: _, expr }) => { ForeignItemKind::Static(box StaticForeignItem {
ty,
mutability: _,
expr,
safety: _,
}) => {
visitor.visit_ty(ty); visitor.visit_ty(ty);
visit_opt(expr, |expr| visitor.visit_expr(expr)); visit_opt(expr, |expr| visitor.visit_expr(expr));
} }

View File

@ -210,6 +210,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> boo
kw::Unsafe, kw::Unsafe,
kw::While, kw::While,
kw::Yield, kw::Yield,
kw::Safe,
kw::Static, kw::Static,
] ]
.contains(&name) .contains(&name)
@ -577,6 +578,7 @@ pub fn can_begin_item(&self) -> bool {
kw::Impl, kw::Impl,
kw::Unsafe, kw::Unsafe,
kw::Const, kw::Const,
kw::Safe,
kw::Static, kw::Static,
kw::Union, kw::Union,
kw::Macro, kw::Macro,

View File

@ -658,7 +658,12 @@ fn walk<'a, V: Visitor<'a>>(
) -> V::Result { ) -> V::Result {
let &Item { id, span, ident, ref vis, .. } = item; let &Item { id, span, ident, ref vis, .. } = item;
match self { match self {
ForeignItemKind::Static(box StaticForeignItem { ty, mutability: _, expr }) => { ForeignItemKind::Static(box StaticForeignItem {
ty,
mutability: _,
expr,
safety: _,
}) => {
try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr); visit_opt!(visitor, visit_expr, expr);
} }

View File

@ -388,7 +388,7 @@ fn lower_item_kind(
ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)), ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)),
}; };
hir::ItemKind::Impl(self.arena.alloc(hir::Impl { hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
safety: self.lower_safety(*safety), safety: self.lower_safety(*safety, hir::Safety::Safe),
polarity, polarity,
defaultness, defaultness,
defaultness_span, defaultness_span,
@ -418,7 +418,7 @@ fn lower_item_kind(
let items = this.arena.alloc_from_iter( let items = this.arena.alloc_from_iter(
items.iter().map(|item| this.lower_trait_item_ref(item)), 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) (safety, items, bounds)
}, },
); );
@ -660,13 +660,21 @@ fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir
this.lower_fn_params_to_names(fdec), 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 let ty = self
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); .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::TyAlias(..) => hir::ForeignItemKind::Type,
ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"), ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
@ -1360,7 +1368,7 @@ pub(super) fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
hir::IsAsync::NotAsync hir::IsAsync::NotAsync
}; };
hir::FnHeader { hir::FnHeader {
safety: self.lower_safety(h.safety), safety: self.lower_safety(h.safety, hir::Safety::Safe),
asyncness: asyncness, asyncness: asyncness,
constness: self.lower_constness(h.constness), constness: self.lower_constness(h.constness),
abi: self.lower_extern(h.ext), abi: self.lower_extern(h.ext),
@ -1410,10 +1418,11 @@ pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
} }
} }
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 { match s {
Safety::Unsafe(_) => hir::Safety::Unsafe, Safety::Unsafe(_) => hir::Safety::Unsafe,
Safety::Default => hir::Safety::Safe, Safety::Default => default,
Safety::Safe(_) => hir::Safety::Safe,
} }
} }

View File

@ -1321,7 +1321,7 @@ fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir>
let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params);
hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy { hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy {
generic_params, generic_params,
safety: self.lower_safety(f.safety), safety: self.lower_safety(f.safety, hir::Safety::Safe),
abi: self.lower_extern(f.ext), abi: self.lower_extern(f.ext),
decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None), decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None),
param_names: self.lower_fn_params_to_names(&f.decl), param_names: self.lower_fn_params_to_names(&f.decl),

View File

@ -1184,7 +1184,7 @@ fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
self.check_foreign_ty_genericless(generics, where_clauses); self.check_foreign_ty_genericless(generics, where_clauses);
self.check_foreign_item_ascii_only(fi.ident); self.check_foreign_item_ascii_only(fi.ident);
} }
ForeignItemKind::Static(box StaticForeignItem { ty: _, mutability: _, expr }) => { ForeignItemKind::Static(box StaticForeignItem { expr, .. }) => {
self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span)); self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span));
self.check_foreign_item_ascii_only(fi.ident); self.check_foreign_item_ascii_only(fi.ident);
} }

View File

@ -1973,6 +1973,7 @@ fn print_fn_header_info(&mut self, header: ast::FnHeader) {
fn print_safety(&mut self, s: ast::Safety) { fn print_safety(&mut self, s: ast::Safety) {
match s { match s {
ast::Safety::Default => {} ast::Safety::Default => {}
ast::Safety::Safe(_) => self.word_nbsp("safe"),
ast::Safety::Unsafe(_) => self.word_nbsp("unsafe"), ast::Safety::Unsafe(_) => self.word_nbsp("unsafe"),
} }
} }

View File

@ -31,7 +31,13 @@ fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); 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( self.print_item_const(
ident, ident,
Some(*mutability), Some(*mutability),

View File

@ -3475,9 +3475,9 @@ pub fn foreign_item_id(&self) -> ForeignItemId {
#[derive(Debug, Clone, Copy, HashStable_Generic)] #[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum ForeignItemKind<'hir> { pub enum ForeignItemKind<'hir> {
/// A foreign function. /// 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`). /// A foreign static item (`static ext: u8`).
Static(&'hir Ty<'hir>, Mutability), Static(&'hir Ty<'hir>, Mutability, Safety),
/// A foreign type. /// A foreign type.
Type, Type,
} }
@ -3545,7 +3545,7 @@ pub fn fn_decl(self) -> Option<&'hir FnDecl<'hir>> {
| OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
| OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl),
OwnerNode::ForeignItem(ForeignItem { OwnerNode::ForeignItem(ForeignItem {
kind: ForeignItemKind::Fn(fn_decl, _, _), kind: ForeignItemKind::Fn(fn_decl, _, _, _),
.. ..
}) => Some(fn_decl), }) => Some(fn_decl),
_ => None, _ => None,
@ -3728,9 +3728,9 @@ pub fn fn_decl(self) -> Option<&'hir FnDecl<'hir>> {
| Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
| Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl),
Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. }) Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. })
| Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { | Node::ForeignItem(ForeignItem {
Some(fn_decl) kind: ForeignItemKind::Fn(fn_decl, _, _, _), ..
} }) => Some(fn_decl),
_ => None, _ => None,
} }
} }
@ -3813,7 +3813,7 @@ pub fn body_id(&self) -> Option<BodyId> {
pub fn generics(self) -> Option<&'hir Generics<'hir>> { pub fn generics(self) -> Option<&'hir Generics<'hir>> {
match self { match self {
Node::ForeignItem(ForeignItem { Node::ForeignItem(ForeignItem {
kind: ForeignItemKind::Fn(_, _, generics), .. kind: ForeignItemKind::Fn(_, _, generics, _), ..
}) })
| Node::TraitItem(TraitItem { generics, .. }) | Node::TraitItem(TraitItem { generics, .. })
| Node::ImplItem(ImplItem { generics, .. }) => Some(generics), | Node::ImplItem(ImplItem { generics, .. }) => Some(generics),

View File

@ -608,12 +608,14 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(
try_visit!(visitor.visit_ident(foreign_item.ident)); try_visit!(visitor.visit_ident(foreign_item.ident));
match foreign_item.kind { 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_generics(generics));
try_visit!(visitor.visit_fn_decl(function_declaration)); try_visit!(visitor.visit_fn_decl(function_declaration));
walk_list!(visitor, visit_ident, param_names.iter().copied()); 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 => (), ForeignItemKind::Type => (),
} }
V::Result::output() V::Result::output()

View File

@ -801,7 +801,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let item = tcx.hir().foreign_item(item.id); let item = tcx.hir().foreign_item(item.id);
match &item.kind { 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); require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span);
} }
hir::ForeignItemKind::Static(..) => { hir::ForeignItemKind::Static(..) => {

View File

@ -29,7 +29,7 @@ fn equate_intrinsic_type<'tcx>(
let (own_counts, span) = match tcx.hir_node_by_def_id(def_id) { 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::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. })
| hir::Node::ForeignItem(hir::ForeignItem { | 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(); let own_counts = tcx.generics_of(def_id).own_counts();

View File

@ -1321,9 +1321,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) 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); 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() => { Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => {
@ -1695,11 +1697,12 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
def_id: LocalDefId, def_id: LocalDefId,
decl: &'tcx hir::FnDecl<'tcx>, decl: &'tcx hir::FnDecl<'tcx>,
abi: abi::Abi, abi: abi::Abi,
safety: hir::Safety,
) -> ty::PolyFnSig<'tcx> { ) -> ty::PolyFnSig<'tcx> {
let safety = if abi == abi::Abi::RustIntrinsic { let safety = if abi == abi::Abi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, def_id) intrinsic_operation_unsafety(tcx, def_id)
} else { } else {
hir::Safety::Unsafe safety
}; };
let hir_id = tcx.local_def_id_to_hir_id(def_id); let hir_id = tcx.local_def_id_to_hir_id(def_id);
let fty = let fty =

View File

@ -603,7 +603,7 @@ fn visit_precise_capturing_arg(
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
match item.kind { match item.kind {
hir::ForeignItemKind::Fn(_, _, generics) => { hir::ForeignItemKind::Fn(_, _, generics, _) => {
self.visit_early_late(item.hir_id(), generics, |this| { self.visit_early_late(item.hir_id(), generics, |this| {
intravisit::walk_foreign_item(this, item); intravisit::walk_foreign_item(this, item);
}) })

View File

@ -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); let args = ty::GenericArgs::identity_for_item(tcx, def_id);
Ty::new_fn_def(tcx, def_id.to_def_id(), args) 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()), ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()),
}, },

View File

@ -158,7 +158,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
}, },
hir::Node::Field(field) => vec![field.ty], hir::Node::Field(field) => vec![field.ty],
hir::Node::ForeignItem(ForeignItem { hir::Node::ForeignItem(ForeignItem {
kind: ForeignItemKind::Static(ty, _), .. kind: ForeignItemKind::Static(ty, _, _), ..
}) => vec![*ty], }) => vec![*ty],
hir::Node::GenericParam(hir::GenericParam { hir::Node::GenericParam(hir::GenericParam {
kind: hir::GenericParamKind::Type { default: Some(ty), .. }, kind: hir::GenericParamKind::Type { default: Some(ty), .. },

View File

@ -345,12 +345,12 @@ fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
self.maybe_print_comment(item.span.lo()); self.maybe_print_comment(item.span.lo());
self.print_outer_attributes(self.attrs(item.hir_id())); self.print_outer_attributes(self.attrs(item.hir_id()));
match item.kind { match item.kind {
hir::ForeignItemKind::Fn(decl, arg_names, generics) => { hir::ForeignItemKind::Fn(decl, arg_names, generics, safety) => {
self.head(""); self.head("");
self.print_fn( self.print_fn(
decl, decl,
hir::FnHeader { hir::FnHeader {
safety: hir::Safety::Safe, safety,
constness: hir::Constness::NotConst, constness: hir::Constness::NotConst,
abi: Abi::Rust, abi: Abi::Rust,
asyncness: hir::IsAsync::NotAsync, asyncness: hir::IsAsync::NotAsync,
@ -364,7 +364,8 @@ fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
self.word(";"); self.word(";");
self.end() // end the outer fn box 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"); self.head("static");
if m.is_mut() { if m.is_mut() {
self.word_space("mut"); self.word_space("mut");

View File

@ -1741,13 +1741,13 @@ fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'
let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
match it.kind { 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); 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); 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 => (), hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
} }
} }

View File

@ -886,7 +886,7 @@ fn named_span(item_span: Span, ident: Ident, generics: Option<&Generics<'_>>) ->
Node::Variant(variant) => named_span(variant.span, variant.ident, None), Node::Variant(variant) => named_span(variant.span, variant.ident, None),
Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)), Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)),
Node::ForeignItem(item) => match item.kind { 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), _ => named_span(item.span, item.ident, None),
}, },
Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)), Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)),

View File

@ -201,7 +201,7 @@ pub fn provide(providers: &mut Providers) {
.. ..
}) })
| Node::ForeignItem(&ForeignItem { | Node::ForeignItem(&ForeignItem {
kind: ForeignItemKind::Fn(_, idents, _), kind: ForeignItemKind::Fn(_, idents, _, _),
.. ..
}) = tcx.hir_node(tcx.local_def_id_to_hir_id(def_id)) }) = tcx.hir_node(tcx.local_def_id_to_hir_id(def_id))
{ {

View File

@ -1221,6 +1221,7 @@ pub fn parse_foreign_item(
ty, ty,
mutability: Mutability::Not, mutability: Mutability::Not,
expr, expr,
safety: Safety::Default,
})) }))
} }
_ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"),
@ -2400,9 +2401,9 @@ pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> b
// `pub` is added in case users got confused with the ordering like `async pub fn`, // `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. // only if it wasn't preceded by `default` as `default pub` is invalid.
let quals: &[Symbol] = if check_pub { 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 { } 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`. self.check_keyword_case(kw::Fn, case) // Definitely an `fn`.
// `$qual fn` or `$qual $qual`: // `$qual fn` or `$qual $qual`:
@ -2537,11 +2538,27 @@ enum WrongKw {
} else if self.check_keyword(kw::Unsafe) { } else if self.check_keyword(kw::Unsafe) {
match safety { match safety {
Safety::Unsafe(sp) => Some(WrongKw::Duplicated(sp)), Safety::Unsafe(sp) => Some(WrongKw::Duplicated(sp)),
Safety::Safe(sp) => {
recover_safety = Safety::Unsafe(self.token.span);
Some(WrongKw::Misplaced(sp))
}
Safety::Default => { Safety::Default => {
recover_safety = Safety::Unsafe(self.token.span); recover_safety = Safety::Unsafe(self.token.span);
Some(WrongKw::Misplaced(ext_start_sp)) 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 { } else {
None None
}; };

View File

@ -1221,6 +1221,8 @@ fn parse_coroutine_kind(&mut self, case: Case) -> Option<CoroutineKind> {
fn parse_safety(&mut self, case: Case) -> Safety { fn parse_safety(&mut self, case: Case) -> Safety {
if self.eat_keyword_case(kw::Unsafe, case) { if self.eat_keyword_case(kw::Unsafe, case) {
Safety::Unsafe(self.prev_token.uninterpolated_span()) Safety::Unsafe(self.prev_token.uninterpolated_span())
} else if self.eat_keyword_case(kw::Safe, case) {
Safety::Safe(self.prev_token.uninterpolated_span())
} else { } else {
Safety::Default Safety::Default
} }

View File

@ -211,9 +211,12 @@ fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
let def_kind = match fi.kind { let def_kind = match fi.kind {
ForeignItemKind::Static(box StaticForeignItem { ty: _, mutability, expr: _ }) => { ForeignItemKind::Static(box StaticForeignItem {
DefKind::Static { mutability, nested: false } ty: _,
} mutability,
expr: _,
safety: _,
}) => DefKind::Static { mutability, nested: false },
ForeignItemKind::Fn(_) => DefKind::Fn, ForeignItemKind::Fn(_) => DefKind::Fn,
ForeignItemKind::TyAlias(_) => DefKind::ForeignTy, ForeignItemKind::TyAlias(_) => DefKind::ForeignTy,
ForeignItemKind::MacCall(_) => return self.visit_macro_invoc(fi.id), ForeignItemKind::MacCall(_) => return self.visit_macro_invoc(fi.id),

View File

@ -103,6 +103,7 @@
MacroRules: "macro_rules", MacroRules: "macro_rules",
Raw: "raw", Raw: "raw",
Reuse: "reuse", Reuse: "reuse",
Safe: "safe",
Union: "union", Union: "union",
Yeet: "yeet", Yeet: "yeet",
} }

View File

@ -3089,7 +3089,9 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
let def_id = item.owner_id.to_def_id(); let def_id = item.owner_id.to_def_id();
cx.with_param_env(def_id, |cx| { cx.with_param_env(def_id, |cx| {
let kind = match item.kind { 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| { let (generics, decl) = enter_impl_trait(cx, |cx| {
// NOTE: generics must be cleaned before args // NOTE: generics must be cleaned before args
let generics = clean_generics(generics, cx); let generics = clean_generics(generics, cx);
@ -3099,7 +3101,9 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
}); });
ForeignFunctionItem(Box::new(Function { decl, generics })) 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 }) ForeignStaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: None })
} }
hir::ForeignItemKind::Type => ForeignTypeItem, hir::ForeignItemKind::Type => ForeignTypeItem,

View File

@ -451,13 +451,15 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
ty: lt, ty: lt,
mutability: lm, mutability: lm,
expr: le, expr: le,
safety: ls,
}), }),
Static(box StaticForeignItem { Static(box StaticForeignItem {
ty: rt, ty: rt,
mutability: rm, mutability: rm,
expr: re, 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 { Fn(box ast::Fn {
defaultness: ld, defaultness: ld,

View File

@ -111,6 +111,7 @@ pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str
pub(crate) fn format_safety(unsafety: ast::Safety) -> &'static str { pub(crate) fn format_safety(unsafety: ast::Safety) -> &'static str {
match unsafety { match unsafety {
ast::Safety::Unsafe(..) => "unsafe ", ast::Safety::Unsafe(..) => "unsafe ",
ast::Safety::Safe(..) => "safe ",
ast::Safety::Default => "", ast::Safety::Default => "",
} }
} }

View File

@ -9,7 +9,7 @@ use ::std::prelude::rust_2015::*;
extern crate std; extern crate std;
extern "C" { extern "C" {
fn foo(x: i32, va1: ...); unsafe fn foo(x: i32, va1: ...);
} }
unsafe extern "C" fn bar(_: i32, mut va2: ...) -> usize { va2.arg::<usize>() } unsafe extern "C" fn bar(_: i32, mut va2: ...) -> usize { va2.arg::<usize>() }

View File

@ -2,5 +2,5 @@
//@ compile-flags: --crate-type lib //@ compile-flags: --crate-type lib
pub async const fn x() {} 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` //~| ERROR functions cannot be both `const` and `async`

View File

@ -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 --> $DIR/no-async-const.rs:4:11
| |
LL | pub async const fn x() {} 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` | help: `const` must come before `async`: `const async`
| |
= note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`

View File

@ -7,11 +7,11 @@ LL | async gen fn foo() {}
= help: pass `--edition 2021` to `rustc` = help: pass `--edition 2021` to `rustc`
= note: for more on editions, read https://doc.rust-lang.org/edition-guide = 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 --> $DIR/async_gen_fn.rs:4:7
| |
LL | async gen fn foo() {} 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 error: aborting due to 2 previous errors

View File

@ -3,7 +3,7 @@
async gen fn foo() {} async gen fn foo() {}
//[none]~^ ERROR: `async fn` is not permitted in Rust 2015 //[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 //[e2024]~^^^ ERROR: gen blocks are experimental
fn main() {} fn main() {}

View File

@ -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 --> $DIR/gen_fn.rs:4:1
| |
LL | gen fn foo() {} LL | gen fn foo() {}
| ^^^ expected one of 9 possible tokens | ^^^ expected one of 10 possible tokens
error: aborting due to 1 previous error error: aborting due to 1 previous error

View File

@ -2,7 +2,7 @@
//@[e2024] compile-flags: --edition 2024 -Zunstable-options //@[e2024] compile-flags: --edition 2024 -Zunstable-options
gen fn foo() {} 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 //[e2024]~^^ ERROR: gen blocks are experimental
fn main() {} fn main() {}

View File

@ -2,8 +2,8 @@ fn main() {}
extern "C" { //~ NOTE while parsing this item list starting here extern "C" { //~ NOTE while parsing this item list starting here
pub pub fn foo(); pub pub fn foo();
//~^ 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`
//~| NOTE expected one of 8 possible tokens //~| NOTE expected one of 9 possible tokens
//~| HELP there is already a visibility modifier, remove one //~| HELP there is already a visibility modifier, remove one
//~| NOTE explicit visibility first seen here //~| NOTE explicit visibility first seen here
} //~ NOTE the item list ends here } //~ NOTE the item list ends here

View File

@ -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 --> $DIR/duplicate-visibility.rs:4:9
| |
LL | extern "C" { LL | extern "C" {
@ -6,7 +6,7 @@ LL | extern "C" {
LL | pub pub fn foo(); 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 | help: there is already a visibility modifier, remove one
... ...
LL | } LL | }

View File

@ -2,6 +2,6 @@
mod t { mod t {
async pub fn 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` //~| HELP visibility `pub` must come before `async`
} }

View File

@ -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 --> $DIR/issue-76437-async.rs:4:11
| |
LL | async pub fn t() {} 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` | help: visibility `pub` must come before `async`: `pub async`
error: aborting due to 1 previous error error: aborting due to 1 previous error

View File

@ -2,6 +2,6 @@
mod t { mod t {
const async pub fn 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` //~| HELP visibility `pub` must come before `const async`
} }

View File

@ -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 --> $DIR/issue-76437-const-async.rs:4:17
| |
LL | const async pub fn t() {} 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` | help: visibility `pub` must come before `const async`: `pub const async`
error: aborting due to 1 previous error error: aborting due to 1 previous error

View File

@ -2,6 +2,6 @@
mod t { mod t {
const pub fn 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` //~| HELP visibility `pub` must come before `const`
} }

View File

@ -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 --> $DIR/issue-76437-const.rs:4:11
| |
LL | const pub fn t() {} 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` | help: visibility `pub` must come before `const`: `pub const`
error: aborting due to 1 previous error error: aborting due to 1 previous error

View File

@ -1,3 +1,3 @@
const pub () {} const pub () {}
//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe` //~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`
pub fn main() {} pub fn main() {}

View File

@ -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 --> $DIR/issue-86895.rs:1:7
| |
LL | const pub () {} 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 error: aborting due to 1 previous error

View File

@ -3,8 +3,8 @@
// Test that even when `const` is already present, the proposed fix is to remove the second `const` // Test that even when `const` is already present, the proposed fix is to remove the second `const`
const async const fn test() {} const async const fn test() {}
//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const` //~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const`
//~| NOTE expected one of `extern`, `fn`, or `unsafe` //~| NOTE expected one of `extern`, `fn`, `safe`, or `unsafe`
//~| HELP `const` already used earlier, remove this one //~| HELP `const` already used earlier, remove this one
//~| NOTE `const` first seen here //~| NOTE `const` first seen here
//~| ERROR functions cannot be both `const` and `async` //~| ERROR functions cannot be both `const` and `async`

View File

@ -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 --> $DIR/const-async-const.rs:5:13
| |
LL | const async const fn test() {} 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 | help: `const` already used earlier, remove this one
| |
note: `const` first seen here note: `const` first seen here

View File

@ -1,5 +1,5 @@
pub const pub fn test() {} pub const pub fn test() {}
//~^ 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`
//~| NOTE expected one of `async`, `extern`, `fn`, or `unsafe` //~| NOTE expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`
//~| HELP there is already a visibility modifier, remove one //~| HELP there is already a visibility modifier, remove one
//~| NOTE explicit visibility first seen here //~| NOTE explicit visibility first seen here

View File

@ -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 --> $DIR/issue-87694-duplicated-pub.rs:1:11
| |
LL | pub const pub fn test() {} 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 | help: there is already a visibility modifier, remove one
| |
note: explicit visibility first seen here note: explicit visibility first seen here

View File

@ -1,5 +1,5 @@
const pub fn test() {} const pub fn test() {}
//~^ 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`
//~| NOTE expected one of `async`, `extern`, `fn`, or `unsafe` //~| NOTE expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`
//~| HELP visibility `pub` must come before `const` //~| HELP visibility `pub` must come before `const`
//~| SUGGESTION pub const //~| SUGGESTION pub const

View File

@ -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 --> $DIR/issue-87694-misplaced-pub.rs:1:7
| |
LL | const pub fn test() {} 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` | help: visibility `pub` must come before `const`: `pub const`
error: aborting due to 1 previous error error: aborting due to 1 previous error

View File

@ -0,0 +1,15 @@
//@ revisions: edition2021 edition2024
//@[edition2021] edition:2021
//@[edition2024] edition:2024
//@[edition2024] compile-flags: -Zunstable-options
//@ check-pass
unsafe extern "C" {
safe fn test1(i: i32);
}
fn test2(i: i32) {
test1(i);
}
fn main() {}