Skip to content

Commit 2b67f01

Browse files
lcnrBoxyUwU
authored andcommitted
check FnDef return type for WF
1 parent 4fd4797 commit 2b67f01

File tree

6 files changed

+138
-44
lines changed

6 files changed

+138
-44
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/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+
//~^ ERROR lifetime may not live long enough
12+
f.baz(x)
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+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/wf-fn-def-check-sig-1.rs:10:13
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+
| ^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
10+
|
11+
= help: consider adding the following bound: `'a: 'b`
12+
13+
error: aborting due to previous error
14+

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 previous error
15+

0 commit comments

Comments
 (0)