Skip to content

Commit fef1fc4

Browse files
authored
Rollup merge of #109441 - oli-obk:fn_trait_new_solver, r=compiler-errors
Only implement Fn* traits for extern "Rust" safe function pointers and items Since calling the function via an `Fn` trait will assume `extern "Rust"` ABI and not do any safety checks, only safe `extern "Rust"` function can implement the `Fn` traits. This syncs the logic between the old solver and the new solver. r? `@compiler-errors`
2 parents 96f35ed + a00413f commit fef1fc4

File tree

5 files changed

+189
-28
lines changed

5 files changed

+189
-28
lines changed

compiler/rustc_middle/src/ty/sty.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_macros::HashStable;
2323
use rustc_span::symbol::{kw, sym, Symbol};
2424
use rustc_span::Span;
2525
use rustc_target::abi::VariantIdx;
26-
use rustc_target::spec::abi;
26+
use rustc_target::spec::abi::{self, Abi};
2727
use std::borrow::Cow;
2828
use std::cmp::Ordering;
2929
use std::fmt;
@@ -1403,6 +1403,18 @@ impl<'tcx> PolyFnSig<'tcx> {
14031403
pub fn abi(&self) -> abi::Abi {
14041404
self.skip_binder().abi
14051405
}
1406+
1407+
pub fn is_fn_trait_compatible(&self) -> bool {
1408+
matches!(
1409+
self.skip_binder(),
1410+
ty::FnSig {
1411+
unsafety: rustc_hir::Unsafety::Normal,
1412+
abi: Abi::Rust,
1413+
c_variadic: false,
1414+
..
1415+
}
1416+
)
1417+
}
14061418
}
14071419

14081420
pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>;

compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,28 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
189189
goal_kind: ty::ClosureKind,
190190
) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> {
191191
match *self_ty.kind() {
192-
ty::FnDef(def_id, substs) => Ok(Some(
193-
tcx.fn_sig(def_id)
194-
.subst(tcx, substs)
195-
.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())),
196-
)),
197-
ty::FnPtr(sig) => Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())))),
192+
// keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
193+
ty::FnDef(def_id, substs) => {
194+
let sig = tcx.fn_sig(def_id);
195+
if sig.skip_binder().is_fn_trait_compatible()
196+
&& tcx.codegen_fn_attrs(def_id).target_features.is_empty()
197+
{
198+
Ok(Some(
199+
sig.subst(tcx, substs)
200+
.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())),
201+
))
202+
} else {
203+
Err(NoSolution)
204+
}
205+
}
206+
// keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
207+
ty::FnPtr(sig) => {
208+
if sig.is_fn_trait_compatible() {
209+
Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output()))))
210+
} else {
211+
Err(NoSolution)
212+
}
213+
}
198214
ty::Closure(_, substs) => {
199215
let closure_substs = substs.as_closure();
200216
match closure_substs.kind_ty().to_opt_closure_kind() {

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+10-20
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use rustc_infer::traits::ObligationCause;
1111
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
1212
use rustc_middle::ty::fast_reject::TreatProjections;
1313
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
14-
use rustc_target::spec::abi::Abi;
1514

1615
use crate::traits;
1716
use crate::traits::query::evaluate_obligation::InferCtxtExt;
@@ -291,6 +290,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
291290
return;
292291
}
293292

293+
// Keep this funtion in sync with extract_tupled_inputs_and_output_from_callable
294+
// until the old solver (and thus this function) is removed.
295+
294296
// Okay to skip binder because what we are inspecting doesn't involve bound regions.
295297
let self_ty = obligation.self_ty().skip_binder();
296298
match *self_ty.kind() {
@@ -299,31 +301,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
299301
candidates.ambiguous = true; // Could wind up being a fn() type.
300302
}
301303
// Provide an impl, but only for suitable `fn` pointers.
302-
ty::FnPtr(_) => {
303-
if let ty::FnSig {
304-
unsafety: hir::Unsafety::Normal,
305-
abi: Abi::Rust,
306-
c_variadic: false,
307-
..
308-
} = self_ty.fn_sig(self.tcx()).skip_binder()
309-
{
304+
ty::FnPtr(sig) => {
305+
if sig.is_fn_trait_compatible() {
310306
candidates.vec.push(FnPointerCandidate { is_const: false });
311307
}
312308
}
313309
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
314310
ty::FnDef(def_id, _) => {
315-
if let ty::FnSig {
316-
unsafety: hir::Unsafety::Normal,
317-
abi: Abi::Rust,
318-
c_variadic: false,
319-
..
320-
} = self_ty.fn_sig(self.tcx()).skip_binder()
311+
if self.tcx().fn_sig(def_id).skip_binder().is_fn_trait_compatible()
312+
&& self.tcx().codegen_fn_attrs(def_id).target_features.is_empty()
321313
{
322-
if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() {
323-
candidates
324-
.vec
325-
.push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) });
326-
}
314+
candidates
315+
.vec
316+
.push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) });
327317
}
328318
}
329319
_ => {}
+20-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
// compile-flags: -Ztrait-solver=next
2-
// check-pass
32

43
fn require_fn(_: impl Fn() -> i32) {}
54

65
fn f() -> i32 {
76
1i32
87
}
98

9+
extern "C" fn g() -> i32 {
10+
2i32
11+
}
12+
13+
unsafe fn h() -> i32 {
14+
2i32
15+
}
16+
1017
fn main() {
1118
require_fn(f);
1219
require_fn(f as fn() -> i32);
20+
require_fn(f as unsafe fn() -> i32);
21+
//~^ ERROR: expected a `Fn<()>` closure, found `unsafe fn() -> i32`
22+
//~| ERROR: type mismatch resolving `<unsafe fn() -> i32 as FnOnce<()>>::Output == i32`
23+
require_fn(g);
24+
//~^ ERROR: expected a `Fn<()>` closure, found `extern "C" fn() -> i32 {g}`
25+
//~| ERROR: type mismatch resolving `<extern "C" fn() -> i32 {g} as FnOnce<()>>::Output == i32`
26+
require_fn(g as extern "C" fn() -> i32);
27+
//~^ ERROR: expected a `Fn<()>` closure, found `extern "C" fn() -> i32`
28+
//~| ERROR: type mismatch resolving `<extern "C" fn() -> i32 as FnOnce<()>>::Output == i32`
29+
require_fn(h);
30+
//~^ ERROR: expected a `Fn<()>` closure, found `unsafe fn() -> i32 {h}`
31+
//~| ERROR: type mismatch resolving `<unsafe fn() -> i32 {h} as FnOnce<()>>::Output == i32`
1332
}
+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
error[E0277]: expected a `Fn<()>` closure, found `unsafe fn() -> i32`
2+
--> $DIR/fn-trait.rs:20:16
3+
|
4+
LL | require_fn(f as unsafe fn() -> i32);
5+
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `Fn<()>` is not implemented for `unsafe fn() -> i32`
10+
= note: wrap the `unsafe fn() -> i32` in a closure with no arguments: `|| { /* code */ }`
11+
note: required by a bound in `require_fn`
12+
--> $DIR/fn-trait.rs:3:23
13+
|
14+
LL | fn require_fn(_: impl Fn() -> i32) {}
15+
| ^^^^^^^^^^^ required by this bound in `require_fn`
16+
17+
error[E0271]: type mismatch resolving `<unsafe fn() -> i32 as FnOnce<()>>::Output == i32`
18+
--> $DIR/fn-trait.rs:20:16
19+
|
20+
LL | require_fn(f as unsafe fn() -> i32);
21+
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^ types differ
22+
| |
23+
| required by a bound introduced by this call
24+
|
25+
note: required by a bound in `require_fn`
26+
--> $DIR/fn-trait.rs:3:31
27+
|
28+
LL | fn require_fn(_: impl Fn() -> i32) {}
29+
| ^^^ required by this bound in `require_fn`
30+
31+
error[E0277]: expected a `Fn<()>` closure, found `extern "C" fn() -> i32 {g}`
32+
--> $DIR/fn-trait.rs:23:16
33+
|
34+
LL | require_fn(g);
35+
| ---------- ^ expected an `Fn<()>` closure, found `extern "C" fn() -> i32 {g}`
36+
| |
37+
| required by a bound introduced by this call
38+
|
39+
= help: the trait `Fn<()>` is not implemented for fn item `extern "C" fn() -> i32 {g}`
40+
= note: wrap the `extern "C" fn() -> i32 {g}` in a closure with no arguments: `|| { /* code */ }`
41+
note: required by a bound in `require_fn`
42+
--> $DIR/fn-trait.rs:3:23
43+
|
44+
LL | fn require_fn(_: impl Fn() -> i32) {}
45+
| ^^^^^^^^^^^ required by this bound in `require_fn`
46+
47+
error[E0271]: type mismatch resolving `<extern "C" fn() -> i32 {g} as FnOnce<()>>::Output == i32`
48+
--> $DIR/fn-trait.rs:23:16
49+
|
50+
LL | require_fn(g);
51+
| ---------- ^ types differ
52+
| |
53+
| required by a bound introduced by this call
54+
|
55+
note: required by a bound in `require_fn`
56+
--> $DIR/fn-trait.rs:3:31
57+
|
58+
LL | fn require_fn(_: impl Fn() -> i32) {}
59+
| ^^^ required by this bound in `require_fn`
60+
61+
error[E0277]: expected a `Fn<()>` closure, found `extern "C" fn() -> i32`
62+
--> $DIR/fn-trait.rs:26:16
63+
|
64+
LL | require_fn(g as extern "C" fn() -> i32);
65+
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `extern "C" fn() -> i32`
66+
| |
67+
| required by a bound introduced by this call
68+
|
69+
= help: the trait `Fn<()>` is not implemented for `extern "C" fn() -> i32`
70+
= note: wrap the `extern "C" fn() -> i32` in a closure with no arguments: `|| { /* code */ }`
71+
note: required by a bound in `require_fn`
72+
--> $DIR/fn-trait.rs:3:23
73+
|
74+
LL | fn require_fn(_: impl Fn() -> i32) {}
75+
| ^^^^^^^^^^^ required by this bound in `require_fn`
76+
77+
error[E0271]: type mismatch resolving `<extern "C" fn() -> i32 as FnOnce<()>>::Output == i32`
78+
--> $DIR/fn-trait.rs:26:16
79+
|
80+
LL | require_fn(g as extern "C" fn() -> i32);
81+
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
82+
| |
83+
| required by a bound introduced by this call
84+
|
85+
note: required by a bound in `require_fn`
86+
--> $DIR/fn-trait.rs:3:31
87+
|
88+
LL | fn require_fn(_: impl Fn() -> i32) {}
89+
| ^^^ required by this bound in `require_fn`
90+
91+
error[E0277]: expected a `Fn<()>` closure, found `unsafe fn() -> i32 {h}`
92+
--> $DIR/fn-trait.rs:29:16
93+
|
94+
LL | require_fn(h);
95+
| ---------- ^ call the function in a closure: `|| unsafe { /* code */ }`
96+
| |
97+
| required by a bound introduced by this call
98+
|
99+
= help: the trait `Fn<()>` is not implemented for fn item `unsafe fn() -> i32 {h}`
100+
= note: wrap the `unsafe fn() -> i32 {h}` in a closure with no arguments: `|| { /* code */ }`
101+
note: required by a bound in `require_fn`
102+
--> $DIR/fn-trait.rs:3:23
103+
|
104+
LL | fn require_fn(_: impl Fn() -> i32) {}
105+
| ^^^^^^^^^^^ required by this bound in `require_fn`
106+
107+
error[E0271]: type mismatch resolving `<unsafe fn() -> i32 {h} as FnOnce<()>>::Output == i32`
108+
--> $DIR/fn-trait.rs:29:16
109+
|
110+
LL | require_fn(h);
111+
| ---------- ^ types differ
112+
| |
113+
| required by a bound introduced by this call
114+
|
115+
note: required by a bound in `require_fn`
116+
--> $DIR/fn-trait.rs:3:31
117+
|
118+
LL | fn require_fn(_: impl Fn() -> i32) {}
119+
| ^^^ required by this bound in `require_fn`
120+
121+
error: aborting due to 8 previous errors
122+
123+
Some errors have detailed explanations: E0271, E0277.
124+
For more information about an error, try `rustc --explain E0271`.

0 commit comments

Comments
 (0)