Skip to content

Commit 81bebb9

Browse files
Implement Fn traits for functions of any ABI
This brings the behavior of the language in line with the documentation in `library/core/src/primitive_docs.rs`.
1 parent 6eeff9a commit 81bebb9

15 files changed

Lines changed: 150 additions & 198 deletions

File tree

‎compiler/rustc_abi/src/extern_abi.rs‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,43 @@ impl ExternAbi {
369369
| Self::Swift => true,
370370
}
371371
}
372+
373+
/// Returns whether safe functions with this ABI implement the `Fn` traits.
374+
/// `false` only for uncallable or internal ABIs.
375+
pub fn is_fn_trait_compatible(self) -> bool {
376+
match self {
377+
ExternAbi::C { .. }
378+
| ExternAbi::System { .. }
379+
| ExternAbi::Rust
380+
| ExternAbi::RustCold
381+
| ExternAbi::RustPreserveNone
382+
| ExternAbi::RustTail
383+
| ExternAbi::EfiApi
384+
| ExternAbi::Swift
385+
| ExternAbi::Aapcs { .. }
386+
| ExternAbi::CmseNonSecureCall
387+
| ExternAbi::CmseNonSecureEntry
388+
| ExternAbi::Cdecl { .. }
389+
| ExternAbi::Stdcall { .. }
390+
| ExternAbi::Fastcall { .. }
391+
| ExternAbi::Thiscall { .. }
392+
| ExternAbi::Vectorcall { .. }
393+
| ExternAbi::SysV64 { .. }
394+
| ExternAbi::Win64 { .. } => true,
395+
ExternAbi::RustCall
396+
| ExternAbi::RustInvalid
397+
| ExternAbi::Unadjusted
398+
| ExternAbi::Custom
399+
| ExternAbi::GpuKernel
400+
| ExternAbi::PtxKernel
401+
| ExternAbi::AvrInterrupt
402+
| ExternAbi::AvrNonBlockingInterrupt
403+
| ExternAbi::Msp430Interrupt
404+
| ExternAbi::RiscvInterruptM
405+
| ExternAbi::RiscvInterruptS
406+
| ExternAbi::X86Interrupt => false,
407+
}
408+
}
372409
}
373410

374411
pub fn all_names() -> Vec<&'static str> {

‎compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use std::path::PathBuf;
55
use std::{debug_assert_matches, iter};
66

77
use itertools::{EitherOrBoth, Itertools};
8-
use rustc_abi::ExternAbi;
98
use rustc_attr_ir::lang_items::{self, LangItem};
109
use rustc_data_structures::fx::FxHashSet;
1110
use rustc_errors::codes::*;
@@ -5490,9 +5489,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
54905489
..
54915490
} = fn_ty.fn_sig(tcx).skip_binder()
54925491
// FIXME(splat): this might need to change if the Fn* traits start using/supporting splat
5493-
&& fn_sig.abi() == ExternAbi::Rust
5494-
&& !fn_sig.c_variadic()
5495-
&& fn_sig.safety() == hir::Safety::Safe
5492+
&& fn_sig.is_fn_trait_compatible()
54965493

54975494
// Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T`
54985495
&& let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind())

‎compiler/rustc_type_ir/src/ty_kind.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ impl<I: Interner> FnSig<I> {
11451145
}
11461146

11471147
pub fn is_fn_trait_compatible(self) -> bool {
1148-
!self.c_variadic() && self.safety().is_safe() && self.abi() == ExternAbi::Rust
1148+
!self.c_variadic() && self.safety().is_safe() && self.abi().is_fn_trait_compatible()
11491149
}
11501150

11511151
/// Set the safety flag.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//! Test implementation of `Fn` traits for functions with non-`Rust` ABI
2+
3+
use std::ops::{Fn, FnMut, FnOnce};
4+
5+
#[repr(C)]
6+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7+
struct Foo(i32, i32);
8+
9+
extern "C" fn square(foo: Foo) -> Foo {
10+
Foo(foo.0 * foo.1, foo.0 - foo.1)
11+
}
12+
13+
fn call_it<F: ?Sized + Fn(Foo) -> Foo>(f: &F, i: Foo) -> Foo {
14+
f(i)
15+
}
16+
17+
fn call_it_mut<F: ?Sized + FnMut(Foo) -> Foo>(f: &mut F, i: Foo) -> Foo {
18+
f(i)
19+
}
20+
21+
fn call_it_once<F: FnOnce(Foo) -> Foo>(f: F, i: Foo) -> Foo {
22+
f(i)
23+
}
24+
25+
fn main() {
26+
assert_eq!(call_it(&square, Foo(20, 10)), Foo(200, 10));
27+
assert_eq!(call_it_mut(&mut square, Foo(21, 9)), Foo(189, 12));
28+
assert_eq!(call_it_once(square, Foo(18, -3)), Foo(-54, 21));
29+
30+
let mut square_ptr: extern "C" fn(Foo) -> Foo = square;
31+
assert_eq!(call_it(&square_ptr, Foo(30, 11)), Foo(330, 19));
32+
assert_eq!(call_it_mut(&mut square_ptr, Foo(273, -1)), Foo(-273, 274));
33+
assert_eq!(call_it_once(square_ptr, Foo(27, 27)), Foo(729, 0));
34+
35+
let mut square_dyn: Box<dyn Fn(Foo) -> Foo> = Box::new(square);
36+
assert_eq!(call_it(&*square_dyn, Foo(1, 3)), Foo(3, -2));
37+
assert_eq!(call_it_mut(&mut *square_dyn, Foo(5, 25)), Foo(125, -20));
38+
assert_eq!(call_it_once(square_dyn, Foo(9, 2)), Foo(18, 7));
39+
}

‎tests/ui/async-await/async-closures/fn-exception.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ fn test(f: impl AsyncFn()) {}
1515

1616
fn main() {
1717
test(unsafety); //~ ERROR the trait bound
18-
test(abi); //~ ERROR the trait bound
18+
test(abi);
1919
}

‎tests/ui/async-await/async-closures/fn-exception.stderr‎

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,6 @@ note: required by a bound in `test`
1212
LL | fn test(f: impl AsyncFn()) {}
1313
| ^^^^^^^^^ required by this bound in `test`
1414

15-
error[E0277]: the trait bound `extern "C" fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {abi}: AsyncFn()` is not satisfied
16-
--> $DIR/fn-exception.rs:18:10
17-
|
18-
LL | test(abi);
19-
| ---- ^^^ the trait `AsyncFn()` is not implemented for fn item `extern "C" fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {abi}`
20-
| |
21-
| required by a bound introduced by this call
22-
|
23-
note: required by a bound in `test`
24-
--> $DIR/fn-exception.rs:14:17
25-
|
26-
LL | fn test(f: impl AsyncFn()) {}
27-
| ^^^^^^^^^ required by this bound in `test`
28-
29-
error: aborting due to 2 previous errors
15+
error: aborting due to 1 previous error
3016

3117
For more information about this error, try `rustc --explain E0277`.
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
//@ run-pass
2+
13
extern "C" fn f() {
24
}
35

4-
fn is_fn<F>(_: F) where F: Fn() {}
6+
fn is_fn<F>(f: F) where F: Fn() {
7+
f();
8+
}
59

610
fn main() {
7-
// extern functions are extern "C" fn
811
let _x: extern "C" fn() = f; // OK
912
is_fn(f);
10-
//~^ ERROR expected an `Fn()` closure, found `extern "C" fn() {f}`
1113
}

‎tests/ui/extern/extern-wrong-value-type.stderr‎

Lines changed: 0 additions & 19 deletions
This file was deleted.

‎tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs‎

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@ fn produces_string() -> Option<String> {
22
Some("my cool string".to_owned())
33
}
44

5-
fn takes_str_but_too_many_refs(_: &&str) -> Option<()> {
5+
fn takes_str_but_too_many_refs(_: &str) -> Option<()> {
66
Some(())
77
}
88

99
fn no_args() -> Option<()> {
1010
Some(())
1111
}
1212

13-
extern "C" fn takes_str_but_wrong_abi(_: &str) -> Option<()> {
14-
Some(())
15-
}
16-
17-
unsafe fn takes_str_but_unsafe(_: &str) -> Option<()> {
13+
unsafe fn takes_str_but_unsafe(_: String) -> Option<()> {
1814
Some(())
1915
}
2016

@@ -23,10 +19,8 @@ struct TypeWithoutDeref;
2319
fn main() {
2420
let _ = produces_string().and_then(takes_str_but_too_many_refs);
2521
//~^ ERROR type mismatch in function arguments
26-
let _ = produces_string().and_then(takes_str_but_wrong_abi);
27-
//~^ ERROR expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}`
2822
let _ = produces_string().and_then(takes_str_but_unsafe);
29-
//~^ ERROR expected an `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}`
23+
//~^ ERROR expected an `FnOnce(String)` closure, found `unsafe fn(String) -> Option<()> {takes_str_but_unsafe}`
3024
let _ = produces_string().and_then(no_args);
3125
//~^ ERROR function is expected to take 1 argument, but it takes 0 arguments
3226
let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs);
Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,42 @@
11
error[E0631]: type mismatch in function arguments
2-
--> $DIR/suggest-option-asderef-unfixable.rs:24:40
2+
--> $DIR/suggest-option-asderef-unfixable.rs:20:40
33
|
4-
LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> {
5-
| ------------------------------------------------------ found signature defined here
4+
LL | fn takes_str_but_too_many_refs(_: &str) -> Option<()> {
5+
| ----------------------------------------------------- found signature defined here
66
...
77
LL | let _ = produces_string().and_then(takes_str_but_too_many_refs);
88
| -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected due to this
99
| |
1010
| required by a bound introduced by this call
1111
|
1212
= note: expected function signature `fn(String) -> _`
13-
found function signature `fn(&&str) -> _`
13+
found function signature `fn(&str) -> _`
1414
note: required by a bound in `Option::<T>::and_then`
1515
--> $SRC_DIR/core/src/option.rs:LL:COL
1616
help: consider wrapping the function in a closure
1717
|
18-
LL | let _ = produces_string().and_then(|arg0: String| takes_str_but_too_many_refs(/* &&str */));
19-
| ++++++++++++++ +++++++++++++
20-
21-
error[E0277]: expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}`
22-
--> $DIR/suggest-option-asderef-unfixable.rs:26:40
23-
|
24-
LL | let _ = produces_string().and_then(takes_str_but_wrong_abi);
25-
| -------- ^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}`
26-
| |
27-
| required by a bound introduced by this call
18+
LL | let _ = produces_string().and_then(|arg0: String| takes_str_but_too_many_refs(/* &str */));
19+
| ++++++++++++++ ++++++++++++
20+
help: call `Option::as_deref()` first
2821
|
29-
= help: the trait `FnOnce(String)` is not implemented for fn item `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}`
30-
note: required by a bound in `Option::<T>::and_then`
31-
--> $SRC_DIR/core/src/option.rs:LL:COL
22+
LL | let _ = produces_string().as_deref().and_then(takes_str_but_too_many_refs);
23+
| +++++++++++
3224

33-
error[E0277]: expected an `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}`
34-
--> $DIR/suggest-option-asderef-unfixable.rs:28:40
25+
error[E0277]: expected an `FnOnce(String)` closure, found `unsafe fn(String) -> Option<()> {takes_str_but_unsafe}`
26+
--> $DIR/suggest-option-asderef-unfixable.rs:22:40
3527
|
3628
LL | let _ = produces_string().and_then(takes_str_but_unsafe);
3729
| -------- ^^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
3830
| |
3931
| required by a bound introduced by this call
4032
|
41-
= help: the trait `FnOnce(String)` is not implemented for fn item `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}`
33+
= help: the trait `FnOnce(String)` is not implemented for fn item `unsafe fn(String) -> Option<()> {takes_str_but_unsafe}`
4234
= note: unsafe function cannot be called generically without an unsafe block
4335
note: required by a bound in `Option::<T>::and_then`
4436
--> $SRC_DIR/core/src/option.rs:LL:COL
4537

4638
error[E0593]: function is expected to take 1 argument, but it takes 0 arguments
47-
--> $DIR/suggest-option-asderef-unfixable.rs:30:40
39+
--> $DIR/suggest-option-asderef-unfixable.rs:24:40
4840
|
4941
LL | fn no_args() -> Option<()> {
5042
| -------------------------- takes 0 arguments
@@ -58,26 +50,26 @@ note: required by a bound in `Option::<T>::and_then`
5850
--> $SRC_DIR/core/src/option.rs:LL:COL
5951

6052
error[E0631]: type mismatch in function arguments
61-
--> $DIR/suggest-option-asderef-unfixable.rs:32:45
53+
--> $DIR/suggest-option-asderef-unfixable.rs:26:45
6254
|
63-
LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> {
64-
| ------------------------------------------------------ found signature defined here
55+
LL | fn takes_str_but_too_many_refs(_: &str) -> Option<()> {
56+
| ----------------------------------------------------- found signature defined here
6557
...
6658
LL | let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs);
6759
| -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected due to this
6860
| |
6961
| required by a bound introduced by this call
7062
|
7163
= note: expected function signature `fn(TypeWithoutDeref) -> _`
72-
found function signature `fn(&&str) -> _`
64+
found function signature `fn(&str) -> _`
7365
note: required by a bound in `Option::<T>::and_then`
7466
--> $SRC_DIR/core/src/option.rs:LL:COL
7567
help: consider wrapping the function in a closure
7668
|
77-
LL | let _ = Some(TypeWithoutDeref).and_then(|arg0: TypeWithoutDeref| takes_str_but_too_many_refs(/* &&str */));
78-
| ++++++++++++++++++++++++ +++++++++++++
69+
LL | let _ = Some(TypeWithoutDeref).and_then(|arg0: TypeWithoutDeref| takes_str_but_too_many_refs(/* &str */));
70+
| ++++++++++++++++++++++++ ++++++++++++
7971

80-
error: aborting due to 5 previous errors
72+
error: aborting due to 4 previous errors
8173

8274
Some errors have detailed explanations: E0277, E0593, E0631.
8375
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)