diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 946f2cafa15f2..efc1fa05c5f92 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -190,14 +190,19 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::FnSig<'hir> { let header = if let Some(local_sig_id) = sig_id.as_local() { match self.resolver.delegation_fn_sigs.get(&local_sig_id) { - Some(sig) => self.lower_fn_header( - sig.header, + Some(sig) => { + let parent = self.tcx.parent(sig_id); // HACK: we override the default safety instead of generating attributes from the ether. // We are not forwarding the attributes, as the delegation fn sigs are collected on the ast, // and here we need the hir attributes. - if sig.target_feature { hir::Safety::Unsafe } else { hir::Safety::Safe }, - &[], - ), + let default_safety = + if sig.target_feature || self.tcx.def_kind(parent) == DefKind::ForeignMod { + hir::Safety::Unsafe + } else { + hir::Safety::Safe + }; + self.lower_fn_header(sig.header, default_safety, &[]) + } None => self.generate_header_error(), } } else { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index d78c874c76607..7f2e7d5ca8385 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -753,6 +753,11 @@ impl UnsafeOpKind { span: Span, suggest_unsafe_block: bool, ) { + if tcx.hir_opt_delegation_sig_id(hir_id.owner.def_id).is_some() { + // The body of the delegation item is synthesized, so it makes no sense + // to emit this lint. + return; + } let parent_id = tcx.hir_get_parent_item(hir_id); let parent_owner = tcx.hir_owner_node(parent_id); let should_suggest = parent_owner.fn_sig().is_some_and(|sig| { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index d28988cd74f10..f3f6c55158068 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5117,12 +5117,18 @@ struct ItemInfoCollector<'a, 'ra, 'tcx> { } impl ItemInfoCollector<'_, '_, '_> { - fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId, attrs: &[Attribute]) { + fn collect_fn_info( + &mut self, + header: FnHeader, + decl: &FnDecl, + id: NodeId, + attrs: &[Attribute], + ) { let sig = DelegationFnSig { - header: sig.header, - param_count: sig.decl.inputs.len(), - has_self: sig.decl.has_self(), - c_variadic: sig.decl.c_variadic(), + header, + param_count: decl.inputs.len(), + has_self: decl.has_self(), + c_variadic: decl.c_variadic(), target_feature: attrs.iter().any(|attr| attr.has_name(sym::target_feature)), }; self.r.delegation_fn_sigs.insert(self.r.local_def_id(id), sig); @@ -5142,7 +5148,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { | ItemKind::Trait(box Trait { generics, .. }) | ItemKind::TraitAlias(generics, _) => { if let ItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.collect_fn_info(sig, item.id, &item.attrs); + self.collect_fn_info(sig.header, &sig.decl, item.id, &item.attrs); } let def_id = self.r.local_def_id(item.id); @@ -5154,8 +5160,17 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { self.r.item_generics_num_lifetimes.insert(def_id, count); } + ItemKind::ForeignMod(ForeignMod { extern_span, safety: _, abi, items }) => { + for foreign_item in items { + if let ForeignItemKind::Fn(box Fn { sig, .. }) = &foreign_item.kind { + let new_header = + FnHeader { ext: Extern::from_abi(*abi, *extern_span), ..sig.header }; + self.collect_fn_info(new_header, &sig.decl, foreign_item.id, &item.attrs); + } + } + } + ItemKind::Mod(..) - | ItemKind::ForeignMod(..) | ItemKind::Static(..) | ItemKind::Use(..) | ItemKind::ExternCrate(..) @@ -5175,7 +5190,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) { if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.collect_fn_info(sig, item.id, &item.attrs); + self.collect_fn_info(sig.header, &sig.decl, item.id, &item.attrs); } visit::walk_assoc_item(self, item, ctxt); } diff --git a/tests/ui/delegation/foreign-fn.rs b/tests/ui/delegation/foreign-fn.rs new file mode 100644 index 0000000000000..1d221da29ceb2 --- /dev/null +++ b/tests/ui/delegation/foreign-fn.rs @@ -0,0 +1,22 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![deny(unsafe_op_in_unsafe_fn)] +#![deny(unused_unsafe)] + +mod to_reuse { + unsafe extern "C" { + pub fn default_unsafe_foo(); + pub unsafe fn unsafe_foo(); + pub safe fn safe_foo(); + } +} + +reuse to_reuse::{default_unsafe_foo, unsafe_foo, safe_foo}; + +fn main() { + let _: extern "C" fn() = default_unsafe_foo; + //~^ ERROR mismatched types + let _: extern "C" fn() = unsafe_foo; + //~^ ERROR mismatched types + let _: extern "C" fn() = safe_foo; +} diff --git a/tests/ui/delegation/foreign-fn.stderr b/tests/ui/delegation/foreign-fn.stderr new file mode 100644 index 0000000000000..f7d3dba4bb1c0 --- /dev/null +++ b/tests/ui/delegation/foreign-fn.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/foreign-fn.rs:17:30 + | +LL | let _: extern "C" fn() = default_unsafe_foo; + | --------------- ^^^^^^^^^^^^^^^^^^ expected safe fn, found unsafe fn + | | + | expected due to this + | + = note: expected fn pointer `extern "C" fn()` + found fn item `unsafe extern "C" fn() {default_unsafe_foo}` + = note: unsafe functions cannot be coerced into safe function pointers + +error[E0308]: mismatched types + --> $DIR/foreign-fn.rs:19:30 + | +LL | let _: extern "C" fn() = unsafe_foo; + | --------------- ^^^^^^^^^^ expected safe fn, found unsafe fn + | | + | expected due to this + | + = note: expected fn pointer `extern "C" fn()` + found fn item `unsafe extern "C" fn() {unsafe_foo}` + = note: unsafe functions cannot be coerced into safe function pointers + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`.