Skip to content

Commit c4d6215

Browse files
committed
check FnDef return type for WF
1 parent bf1e3f3 commit c4d6215

File tree

6 files changed

+136
-45
lines changed

6 files changed

+136
-45
lines changed

compiler/rustc_trait_selection/src/traits/wf.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
518518
/// Pushes all the predicates needed to validate that `ty` is WF into `out`.
519519
#[instrument(level = "debug", skip(self))]
520520
fn compute(&mut self, arg: GenericArg<'tcx>) {
521+
let tcx = self.tcx();
521522
let mut walker = arg.walk();
522523
let param_env = self.param_env;
523524
let depth = self.recursion_depth;
@@ -542,7 +543,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
542543
));
543544
let cause = self.cause(traits::WellFormed(None));
544545
self.out.push(traits::Obligation::with_depth(
545-
self.tcx(),
546+
tcx,
546547
cause,
547548
self.recursion_depth,
548549
self.param_env,
@@ -554,7 +555,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
554555
let cause = self.cause(traits::WellFormed(None));
555556

556557
self.out.push(traits::Obligation::with_depth(
557-
self.tcx(),
558+
tcx,
558559
cause,
559560
self.recursion_depth,
560561
self.param_env,
@@ -576,7 +577,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
576577
));
577578
let cause = self.cause(traits::WellFormed(None));
578579
self.out.push(traits::Obligation::with_depth(
579-
self.tcx(),
580+
tcx,
580581
cause,
581582
self.recursion_depth,
582583
self.param_env,
@@ -661,6 +662,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
661662
}
662663

663664
ty::FnDef(did, args) => {
665+
// HACK: Check the return type of function definitions for
666+
// well-formedness to mostly fix #84533. This is still not
667+
// perfect and there may be ways to abuse the fact that we
668+
// ignore requirements with escaping bound vars. That's a
669+
// more general issue however.
670+
//
671+
// FIXME(eddyb) add the type to `walker` instead of recursing.
672+
let fn_sig = tcx.fn_sig(did).instantiate(tcx, args);
673+
self.compute(fn_sig.output().skip_binder().into());
674+
664675
let obligations = self.nominal_obligations(did, args);
665676
self.out.extend(obligations);
666677
}
@@ -670,7 +681,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
670681
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
671682
let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
672683
self.out.push(traits::Obligation::with_depth(
673-
self.tcx(),
684+
tcx,
674685
cause,
675686
depth,
676687
param_env,
@@ -745,7 +756,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
745756
// All of the requirements on type parameters
746757
// have already been checked for `impl Trait` in
747758
// return position. We do need to check type-alias-impl-trait though.
748-
if self.tcx().is_type_alias_impl_trait(def_id) {
759+
if tcx.is_type_alias_impl_trait(def_id) {
749760
let obligations = self.nominal_obligations(def_id, args);
750761
self.out.extend(obligations);
751762
}
@@ -767,12 +778,12 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
767778
// obligations that don't refer to Self and
768779
// checking those
769780

770-
let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
781+
let defer_to_coercion = tcx.features().object_safe_for_dispatch;
771782

772783
if !defer_to_coercion {
773784
let cause = self.cause(traits::WellFormed(None));
774785
let component_traits = data.auto_traits().chain(data.principal_def_id());
775-
let tcx = self.tcx();
786+
let tcx = tcx;
776787
self.out.extend(component_traits.map(|did| {
777788
traits::Obligation::with_depth(
778789
tcx,
@@ -800,7 +811,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
800811
ty::Infer(_) => {
801812
let cause = self.cause(traits::WellFormed(None));
802813
self.out.push(traits::Obligation::with_depth(
803-
self.tcx(),
814+
tcx,
804815
cause,
805816
self.recursion_depth,
806817
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)