Skip to content

Commit

Permalink
check FnDef return type for WF
Browse files Browse the repository at this point in the history
  • Loading branch information
lcnr committed Sep 5, 2023
1 parent bf1e3f3 commit c4d6215
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 45 deletions.
27 changes: 19 additions & 8 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 518,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
/// Pushes all the predicates needed to validate that `ty` is WF into `out`.
#[instrument(level = "debug", skip(self))]
fn compute(&mut self, arg: GenericArg<'tcx>) {
let tcx = self.tcx();
let mut walker = arg.walk();
let param_env = self.param_env;
let depth = self.recursion_depth;
Expand All @@ -542,7 543,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
self.param_env,
Expand All @@ -554,7 555,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let cause = self.cause(traits::WellFormed(None));

self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
self.param_env,
Expand All @@ -576,7 577,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
self.param_env,
Expand Down Expand Up @@ -661,6 662,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
}

ty::FnDef(did, args) => {
// HACK: Check the return type of function definitions for
// well-formedness to mostly fix #84533. This is still not
// perfect and there may be ways to abuse the fact that we
// ignore requirements with escaping bound vars. That's a
// more general issue however.
//
// FIXME(eddyb) add the type to `walker` instead of recursing.
let fn_sig = tcx.fn_sig(did).instantiate(tcx, args);
self.compute(fn_sig.output().skip_binder().into());

let obligations = self.nominal_obligations(did, args);
self.out.extend(obligations);
}
Expand All @@ -670,7 681,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
depth,
param_env,
Expand Down Expand Up @@ -745,7 756,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// All of the requirements on type parameters
// have already been checked for `impl Trait` in
// return position. We do need to check type-alias-impl-trait though.
if self.tcx().is_type_alias_impl_trait(def_id) {
if tcx.is_type_alias_impl_trait(def_id) {
let obligations = self.nominal_obligations(def_id, args);
self.out.extend(obligations);
}
Expand All @@ -767,12 778,12 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// obligations that don't refer to Self and
// checking those

let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
let defer_to_coercion = tcx.features().object_safe_for_dispatch;

if !defer_to_coercion {
let cause = self.cause(traits::WellFormed(None));
let component_traits = data.auto_traits().chain(data.principal_def_id());
let tcx = self.tcx();
let tcx = tcx;
self.out.extend(component_traits.map(|did| {
traits::Obligation::with_depth(
tcx,
Expand Down Expand Up @@ -800,7 811,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
ty::Infer(_) => {
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
param_env,
Expand Down
37 changes: 0 additions & 37 deletions tests/ui/fn/fn-item-lifetime-bounds.rs

This file was deleted.

44 changes: 44 additions & 0 deletions tests/ui/wf/wf-fn-def-check-sig-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 1,44 @@
// Regression test for #84533.

use std::marker::PhantomData;

fn foo<'b, 'a>() -> PhantomData<&'b &'a ()> {
PhantomData
}

fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
let f = foo::<'b, 'a>;
//~^ ERROR lifetime may not live long enough
f.baz(x)
}

trait Foo<'a, 'b, T: ?Sized> {
fn baz(self, s: &'a T) -> &'b T;
}
impl<'a, 'b, R, F, T: ?Sized> Foo<'a, 'b, T> for F
where
F: Fn() -> R,
R: ProofForConversion<'a, 'b, T>,
{
fn baz(self, s: &'a T) -> &'b T {
self().convert(s)
}
}

trait ProofForConversion<'a, 'b, T: ?Sized> {
fn convert(self, s: &'a T) -> &'b T;
}
impl<'a, 'b, T: ?Sized> ProofForConversion<'a, 'b, T> for PhantomData<&'b &'a ()> {
fn convert(self, s: &'a T) -> &'b T {
s
}
}

fn main() {
let d;
{
let x = "Hello World".to_string();
d = extend_lifetime(&x);
}
println!("{}", d);
}
14 changes: 14 additions & 0 deletions tests/ui/wf/wf-fn-def-check-sig-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 1,14 @@
error: lifetime may not live long enough
--> $DIR/wf-fn-def-check-sig-1.rs:10:13
|
LL | fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | let f = foo::<'b, 'a>;
| ^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`

error: aborting due to previous error

44 changes: 44 additions & 0 deletions tests/ui/wf/wf-fn-def-check-sig-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 1,44 @@
// Regression test for #84533 involving higher-ranked regions
// in the return type.
use std::marker::PhantomData;

fn foo<'c, 'b, 'a>(_: &'c ()) -> (&'c (), PhantomData<&'b &'a ()>) {
(&(), PhantomData)
}

fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
let f = foo;
f.baz(x)
//~^ ERROR lifetime may not live long enough
}

trait Foo<'a, 'b, T: ?Sized> {
fn baz(self, s: &'a T) -> &'b T;
}
impl<'a, 'b, R, F, T: ?Sized> Foo<'a, 'b, T> for F
where
F: for<'c> Fn(&'c ()) -> (&'c (), R),
R: ProofForConversion<'a, 'b, T>,
{
fn baz(self, s: &'a T) -> &'b T {
self(&()).1.convert(s)
}
}

trait ProofForConversion<'a, 'b, T: ?Sized> {
fn convert(self, s: &'a T) -> &'b T;
}
impl<'a, 'b, T: ?Sized> ProofForConversion<'a, 'b, T> for PhantomData<&'b &'a ()> {
fn convert(self, s: &'a T) -> &'b T {
s
}
}

fn main() {
let d;
{
let x = "Hello World".to_string();
d = extend_lifetime(&x);
}
println!("{}", d);
}
15 changes: 15 additions & 0 deletions tests/ui/wf/wf-fn-def-check-sig-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 1,15 @@
error: lifetime may not live long enough
--> $DIR/wf-fn-def-check-sig-2.rs:11:5
|
LL | fn extend_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | let f = foo;
LL | f.baz(x)
| ^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`

error: aborting due to previous error

0 comments on commit c4d6215

Please sign in to comment.