Skip to content

Commit 3b573c9

Browse files
fn pointers don't implement Fn/FnMut/FnOnce traits if its return type is unsized
1 parent 98e1f04 commit 3b573c9

File tree

5 files changed

+96
-2
lines changed

5 files changed

+96
-2
lines changed

Diff for: compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
617617
)
618618
.map_bound(|(trait_ref, _)| trait_ref);
619619

620-
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
620+
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
621+
622+
// Confirm the `type Output: Sized;` bound that is present on `FnOnce`
623+
let cause = obligation.derived_cause(BuiltinDerivedObligation);
624+
// The binder on the Fn obligation is "less" important than the one on
625+
// the signature, as evidenced by how we treat it during projection.
626+
// The safe thing to do here is to liberate it, though, which should
627+
// have no worse effect than skipping the binder here.
628+
let liberated_fn_ty = self.infcx.replace_bound_vars_with_placeholders(obligation.self_ty());
629+
let output_ty = self
630+
.infcx
631+
.replace_bound_vars_with_placeholders(liberated_fn_ty.fn_sig(self.tcx()).output());
632+
let output_ty = normalize_with_depth_to(
633+
self,
634+
obligation.param_env,
635+
cause.clone(),
636+
obligation.recursion_depth,
637+
output_ty,
638+
&mut nested,
639+
);
640+
let tr = ty::Binder::dummy(ty::TraitRef::new(
641+
self.tcx().require_lang_item(LangItem::Sized, None),
642+
self.tcx().mk_substs_trait(output_ty, &[]),
643+
));
644+
nested.push(Obligation::new(
645+
cause,
646+
obligation.param_env,
647+
tr.to_poly_trait_predicate().to_predicate(self.tcx()),
648+
));
649+
621650
Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested })
622651
}
623652

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// check-pass
2+
3+
#![feature(unboxed_closures)]
4+
5+
fn is_fn<T: for<'a> Fn<(&'a (),)>>() {}
6+
fn is_fn2<T: for<'a, 'b> Fn<(&'a &'b (),)>>() {}
7+
8+
struct Outlives<'a, 'b>(std::marker::PhantomData<&'a &'b ()>);
9+
10+
fn main() {
11+
is_fn::<for<'a> fn(&'a ()) -> &'a ()>();
12+
is_fn::<for<'a> fn(&'a ()) -> &'a dyn std::fmt::Debug>();
13+
is_fn2::<for<'a, 'b> fn(&'a &'b ()) -> Outlives<'a, 'b>>();
14+
is_fn2::<for<'a, 'b> fn(&'a &'b ()) -> (&'a (), &'a ())>();
15+
}

Diff for: src/test/ui/function-pointer/unsized-ret.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(fn_traits)]
2+
#![feature(unboxed_closures)]
3+
4+
fn foo<F: Fn<T>, T>(f: Option<F>, t: T) {
5+
let y = (f.unwrap()).call(t);
6+
}
7+
8+
fn main() {
9+
foo::<fn() -> str, _>(None, ());
10+
//~^ ERROR the size for values of type `str` cannot be known at compilation time
11+
12+
foo::<for<'a> fn(&'a ()) -> (dyn std::fmt::Display + 'a), _>(None, (&(),));
13+
//~^ ERROR the size for values of type `(dyn std::fmt::Display + 'a)` cannot be known at compilation time
14+
}

Diff for: src/test/ui/function-pointer/unsized-ret.stderr

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0277]: the size for values of type `str` cannot be known at compilation time
2+
--> $DIR/unsized-ret.rs:9:27
3+
|
4+
LL | foo::<fn() -> str, _>(None, ());
5+
| --------------------- ^^^^ doesn't have a size known at compile-time
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: within `fn() -> str`, the trait `Sized` is not implemented for `str`
10+
= note: required because it appears within the type `fn() -> str`
11+
note: required by a bound in `foo`
12+
--> $DIR/unsized-ret.rs:4:11
13+
|
14+
LL | fn foo<F: Fn<T>, T>(f: Option<F>, t: T) {
15+
| ^^^^^ required by this bound in `foo`
16+
17+
error[E0277]: the size for values of type `(dyn std::fmt::Display + 'a)` cannot be known at compilation time
18+
--> $DIR/unsized-ret.rs:12:66
19+
|
20+
LL | foo::<for<'a> fn(&'a ()) -> (dyn std::fmt::Display + 'a), _>(None, (&(),));
21+
| ------------------------------------------------------------ ^^^^ doesn't have a size known at compile-time
22+
| |
23+
| required by a bound introduced by this call
24+
|
25+
= help: within `for<'a> fn(&'a ()) -> (dyn std::fmt::Display + 'a)`, the trait `for<'a> Sized` is not implemented for `(dyn std::fmt::Display + 'a)`
26+
= note: required because it appears within the type `for<'a> fn(&'a ()) -> (dyn std::fmt::Display + 'a)`
27+
note: required by a bound in `foo`
28+
--> $DIR/unsized-ret.rs:4:11
29+
|
30+
LL | fn foo<F: Fn<T>, T>(f: Option<F>, t: T) {
31+
| ^^^^^ required by this bound in `foo`
32+
33+
error: aborting due to 2 previous errors
34+
35+
For more information about this error, try `rustc --explain E0277`.

Diff for: src/test/ui/type-alias-impl-trait/issue-53398-cyclic-types.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
error[E0275]: overflow evaluating the requirement `fn() -> Foo {foo}: Sized`
1+
error[E0275]: overflow evaluating the requirement `Foo: Sized`
22
--> $DIR/issue-53398-cyclic-types.rs:5:13
33
|
44
LL | fn foo() -> Foo {
55
| ^^^
66
|
77
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_53398_cyclic_types`)
8+
= note: required because it appears within the type `fn() -> Foo {foo}`
89

910
error: aborting due to previous error
1011

0 commit comments

Comments
 (0)