1
Fork 0

Rollup merge of #138399 - Bryanskiy:delegation-extern-fn, r=petrochenkov

Delegation: allow foreign fns `reuse`

In example:
```rust
unsafe extern "C" {
    fn foo();
}

reuse foo as bar;
```

Desugaring before:

```rust
fn bar() {
    foo()
    //~^ ERROR call to unsafe function `foo` is unsafe and requires unsafe function or block
}
```

after:

```rust
unsafe extern "C" fn bar() {
    foo()
}
```

Fixes https://github.com/rust-lang/rust/issues/127412

r? `@petrochenkov`
This commit is contained in:
Matthias Krüger 2025-03-13 17:44:07 +01:00 committed by GitHub
commit 41d6e6e8da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 87 additions and 13 deletions

View file

@ -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 {

View file

@ -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| {

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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`.