Skip to content

Commit 4c6c629

Browse files
committed
Auto merge of #115538 - lcnr:fn-def-wf, r=compiler-errors
check `FnDef` return type for WF better version of #106807, fixes #84533 (mostly). It's not perfect given that we still ignore WF requirements involving bound regions but I wasn't able to quickly write an example, so even if theoretically exploitable, it should be far harder to trigger. This is strictly more restrictive than checking the return type for WF as part of the builtin `FnDef: FnOnce` impl (#106807) and moving to this approach in the future will not break any code. ~~It also agrees with my theoretical view of how this should behave~~ r? types
2 parents 29fe618 + 8278976 commit 4c6c629

8 files changed

+153
-45
lines changed

compiler/rustc_trait_selection/src/traits/wf.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
647647
fn visit_ty(&mut self, t: <TyCtxt<'tcx> as ty::Interner>::Ty) -> Self::Result {
648648
debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
649649

650+
let tcx = self.tcx();
651+
650652
match *t.kind() {
651653
ty::Bool
652654
| ty::Char
@@ -707,6 +709,16 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
707709
}
708710

709711
ty::FnDef(did, args) => {
712+
// HACK: Check the return type of function definitions for
713+
// well-formedness to mostly fix #84533. This is still not
714+
// perfect and there may be ways to abuse the fact that we
715+
// ignore requirements with escaping bound vars. That's a
716+
// more general issue however.
717+
//
718+
// FIXME(eddyb) add the type to `walker` instead of recursing.
719+
let fn_sig = tcx.fn_sig(did).instantiate(tcx, args);
720+
fn_sig.output().skip_binder().visit_with(self);
721+
710722
let obligations = self.nominal_obligations(did, args);
711723
self.out.extend(obligations);
712724
}
@@ -716,7 +728,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
716728
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
717729
let cause = self.cause(traits::ReferenceOutlivesReferent(t));
718730
self.out.push(traits::Obligation::with_depth(
719-
self.tcx(),
731+
tcx,
720732
cause,
721733
self.recursion_depth,
722734
self.param_env,
@@ -805,12 +817,12 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
805817
// obligations that don't refer to Self and
806818
// checking those
807819

808-
let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
820+
let defer_to_coercion = tcx.features().object_safe_for_dispatch;
809821

810822
if !defer_to_coercion {
811823
if let Some(principal) = data.principal_def_id() {
812824
self.out.push(traits::Obligation::with_depth(
813-
self.tcx(),
825+
tcx,
814826
self.cause(traits::WellFormed(None)),
815827
self.recursion_depth,
816828
self.param_env,
@@ -835,7 +847,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
835847
ty::Infer(_) => {
836848
let cause = self.cause(traits::WellFormed(None));
837849
self.out.push(traits::Obligation::with_depth(
838-
self.tcx(),
850+
tcx,
839851
cause,
840852
self.recursion_depth,
841853
self.param_env,
@@ -850,6 +862,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
850862
}
851863

852864
fn visit_const(&mut self, c: <TyCtxt<'tcx> as ty::Interner>::Const) -> Self::Result {
865+
let tcx = self.tcx();
866+
853867
match c.kind() {
854868
ty::ConstKind::Unevaluated(uv) => {
855869
if !c.has_escaping_bound_vars() {
@@ -861,7 +875,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
861875
));
862876
let cause = self.cause(traits::WellFormed(None));
863877
self.out.push(traits::Obligation::with_depth(
864-
self.tcx(),
878+
tcx,
865879
cause,
866880
self.recursion_depth,
867881
self.param_env,
@@ -873,7 +887,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
873887
let cause = self.cause(traits::WellFormed(None));
874888

875889
self.out.push(traits::Obligation::with_depth(
876-
self.tcx(),
890+
tcx,
877891
cause,
878892
self.recursion_depth,
879893
self.param_env,
@@ -895,7 +909,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
895909
));
896910
let cause = self.cause(traits::WellFormed(None));
897911
self.out.push(traits::Obligation::with_depth(
898-
self.tcx(),
912+
tcx,
899913
cause,
900914
self.recursion_depth,
901915
self.param_env,

tests/ui/fn/fn-item-lifetime-bounds.rs

-37
This file was deleted.

tests/ui/proc-macro/bad-projection.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ pub fn uwu() -> <() as Project>::Assoc {}
1515
//~^ ERROR the trait bound `(): Project` is not satisfied
1616
//~| ERROR the trait bound `(): Project` is not satisfied
1717
//~| ERROR the trait bound `(): Project` is not satisfied
18+
//~| ERROR the trait bound `(): Project` is not satisfied
1819
//~| ERROR function is expected to take 1 argument, but it takes 0 arguments

tests/ui/proc-macro/bad-projection.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ help: this trait has no implementations, consider adding one
3535
LL | trait Project {
3636
| ^^^^^^^^^^^^^
3737

38+
error[E0277]: the trait bound `(): Project` is not satisfied
39+
--> $DIR/bad-projection.rs:14:1
40+
|
41+
LL | pub fn uwu() -> <() as Project>::Assoc {}
42+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Project` is not implemented for `()`
43+
|
44+
help: this trait has no implementations, consider adding one
45+
--> $DIR/bad-projection.rs:9:1
46+
|
47+
LL | trait Project {
48+
| ^^^^^^^^^^^^^
49+
3850
error[E0277]: the trait bound `(): Project` is not satisfied
3951
--> $DIR/bad-projection.rs:14:40
4052
|
@@ -47,7 +59,7 @@ help: this trait has no implementations, consider adding one
4759
LL | trait Project {
4860
| ^^^^^^^^^^^^^
4961

50-
error: aborting due to 4 previous errors
62+
error: aborting due to 5 previous errors
5163

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

tests/ui/wf/wf-fn-def-check-sig-1.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Regression test for #84533.
2+
3+
use std::marker::PhantomData;
4+
5+
fn foo<'b, 'a>() -> PhantomData<&'b &'a ()> {
6+
PhantomData
7+
}
8+
9+
fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
10+
let f = foo::<'b, 'a>;
11+
f.baz(x)
12+
//~^ ERROR lifetime may not live long enough
13+
}
14+
15+
trait Foo<'a, 'b, T: ?Sized> {
16+
fn baz(self, s: &'a T) -> &'b T;
17+
}
18+
impl<'a, 'b, R, F, T: ?Sized> Foo<'a, 'b, T> for F
19+
where
20+
F: Fn() -> R,
21+
R: ProofForConversion<'a, 'b, T>,
22+
{
23+
fn baz(self, s: &'a T) -> &'b T {
24+
self().convert(s)
25+
}
26+
}
27+
28+
trait ProofForConversion<'a, 'b, T: ?Sized> {
29+
fn convert(self, s: &'a T) -> &'b T;
30+
}
31+
impl<'a, 'b, T: ?Sized> ProofForConversion<'a, 'b, T> for PhantomData<&'b &'a ()> {
32+
fn convert(self, s: &'a T) -> &'b T {
33+
s
34+
}
35+
}
36+
37+
fn main() {
38+
let d;
39+
{
40+
let x = "Hello World".to_string();
41+
d = extend_lifetime(&x);
42+
}
43+
println!("{}", d);
44+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/wf-fn-def-check-sig-1.rs:11:5
3+
|
4+
LL | fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
5+
| -- -- lifetime `'b` defined here
6+
| |
7+
| lifetime `'a` defined here
8+
LL | let f = foo::<'b, 'a>;
9+
LL | f.baz(x)
10+
| ^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
11+
|
12+
= help: consider adding the following bound: `'a: 'b`
13+
14+
error: aborting due to 1 previous error
15+

tests/ui/wf/wf-fn-def-check-sig-2.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Regression test for #84533 involving higher-ranked regions
2+
// in the return type.
3+
use std::marker::PhantomData;
4+
5+
fn foo<'c, 'b, 'a>(_: &'c ()) -> (&'c (), PhantomData<&'b &'a ()>) {
6+
(&(), PhantomData)
7+
}
8+
9+
fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
10+
let f = foo;
11+
f.baz(x)
12+
//~^ ERROR lifetime may not live long enough
13+
}
14+
15+
trait Foo<'a, 'b, T: ?Sized> {
16+
fn baz(self, s: &'a T) -> &'b T;
17+
}
18+
impl<'a, 'b, R, F, T: ?Sized> Foo<'a, 'b, T> for F
19+
where
20+
F: for<'c> Fn(&'c ()) -> (&'c (), R),
21+
R: ProofForConversion<'a, 'b, T>,
22+
{
23+
fn baz(self, s: &'a T) -> &'b T {
24+
self(&()).1.convert(s)
25+
}
26+
}
27+
28+
trait ProofForConversion<'a, 'b, T: ?Sized> {
29+
fn convert(self, s: &'a T) -> &'b T;
30+
}
31+
impl<'a, 'b, T: ?Sized> ProofForConversion<'a, 'b, T> for PhantomData<&'b &'a ()> {
32+
fn convert(self, s: &'a T) -> &'b T {
33+
s
34+
}
35+
}
36+
37+
fn main() {
38+
let d;
39+
{
40+
let x = "Hello World".to_string();
41+
d = extend_lifetime(&x);
42+
}
43+
println!("{}", d);
44+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/wf-fn-def-check-sig-2.rs:11:5
3+
|
4+
LL | fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
5+
| -- -- lifetime `'b` defined here
6+
| |
7+
| lifetime `'a` defined here
8+
LL | let f = foo;
9+
LL | f.baz(x)
10+
| ^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
11+
|
12+
= help: consider adding the following bound: `'a: 'b`
13+
14+
error: aborting due to 1 previous error
15+

0 commit comments

Comments
 (0)