mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-06 04:08:40 +00:00
check FnDef
return type for WF
This commit is contained in:
parent
4fd4797c26
commit
2b67f0104a
@ -647,6 +647,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
fn visit_ty(&mut self, t: <TyCtxt<'tcx> as ty::Interner>::Ty) -> Self::Result {
|
fn visit_ty(&mut self, t: <TyCtxt<'tcx> as ty::Interner>::Ty) -> Self::Result {
|
||||||
debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
|
debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
|
||||||
|
|
||||||
|
let tcx = self.tcx();
|
||||||
|
|
||||||
match *t.kind() {
|
match *t.kind() {
|
||||||
ty::Bool
|
ty::Bool
|
||||||
| ty::Char
|
| ty::Char
|
||||||
@ -707,6 +709,16 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ty::FnDef(did, args) => {
|
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);
|
||||||
|
fn_sig.output().skip_binder().visit_with(self);
|
||||||
|
|
||||||
let obligations = self.nominal_obligations(did, args);
|
let obligations = self.nominal_obligations(did, args);
|
||||||
self.out.extend(obligations);
|
self.out.extend(obligations);
|
||||||
}
|
}
|
||||||
@ -716,7 +728,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
|
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
|
||||||
let cause = self.cause(traits::ReferenceOutlivesReferent(t));
|
let cause = self.cause(traits::ReferenceOutlivesReferent(t));
|
||||||
self.out.push(traits::Obligation::with_depth(
|
self.out.push(traits::Obligation::with_depth(
|
||||||
self.tcx(),
|
tcx,
|
||||||
cause,
|
cause,
|
||||||
self.recursion_depth,
|
self.recursion_depth,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
@ -805,12 +817,12 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
// obligations that don't refer to Self and
|
// obligations that don't refer to Self and
|
||||||
// checking those
|
// 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 {
|
if !defer_to_coercion {
|
||||||
if let Some(principal) = data.principal_def_id() {
|
if let Some(principal) = data.principal_def_id() {
|
||||||
self.out.push(traits::Obligation::with_depth(
|
self.out.push(traits::Obligation::with_depth(
|
||||||
self.tcx(),
|
tcx,
|
||||||
self.cause(traits::WellFormed(None)),
|
self.cause(traits::WellFormed(None)),
|
||||||
self.recursion_depth,
|
self.recursion_depth,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
@ -835,7 +847,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
ty::Infer(_) => {
|
ty::Infer(_) => {
|
||||||
let cause = self.cause(traits::WellFormed(None));
|
let cause = self.cause(traits::WellFormed(None));
|
||||||
self.out.push(traits::Obligation::with_depth(
|
self.out.push(traits::Obligation::with_depth(
|
||||||
self.tcx(),
|
tcx,
|
||||||
cause,
|
cause,
|
||||||
self.recursion_depth,
|
self.recursion_depth,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
@ -850,6 +862,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_const(&mut self, c: <TyCtxt<'tcx> as ty::Interner>::Const) -> Self::Result {
|
fn visit_const(&mut self, c: <TyCtxt<'tcx> as ty::Interner>::Const) -> Self::Result {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
|
||||||
match c.kind() {
|
match c.kind() {
|
||||||
ty::ConstKind::Unevaluated(uv) => {
|
ty::ConstKind::Unevaluated(uv) => {
|
||||||
if !c.has_escaping_bound_vars() {
|
if !c.has_escaping_bound_vars() {
|
||||||
@ -861,7 +875,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
));
|
));
|
||||||
let cause = self.cause(traits::WellFormed(None));
|
let cause = self.cause(traits::WellFormed(None));
|
||||||
self.out.push(traits::Obligation::with_depth(
|
self.out.push(traits::Obligation::with_depth(
|
||||||
self.tcx(),
|
tcx,
|
||||||
cause,
|
cause,
|
||||||
self.recursion_depth,
|
self.recursion_depth,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
@ -873,7 +887,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
let cause = self.cause(traits::WellFormed(None));
|
let cause = self.cause(traits::WellFormed(None));
|
||||||
|
|
||||||
self.out.push(traits::Obligation::with_depth(
|
self.out.push(traits::Obligation::with_depth(
|
||||||
self.tcx(),
|
tcx,
|
||||||
cause,
|
cause,
|
||||||
self.recursion_depth,
|
self.recursion_depth,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
@ -895,7 +909,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
|
|||||||
));
|
));
|
||||||
let cause = self.cause(traits::WellFormed(None));
|
let cause = self.cause(traits::WellFormed(None));
|
||||||
self.out.push(traits::Obligation::with_depth(
|
self.out.push(traits::Obligation::with_depth(
|
||||||
self.tcx(),
|
tcx,
|
||||||
cause,
|
cause,
|
||||||
self.recursion_depth,
|
self.recursion_depth,
|
||||||
self.param_env,
|
self.param_env,
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
//@ check-pass
|
|
||||||
//@ known-bug: #84533
|
|
||||||
|
|
||||||
// Should fail. Lifetimes are checked correctly when `foo` is called, but NOT
|
|
||||||
// when only the lifetime parameters are instantiated.
|
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn foo<'b, 'a>() -> PhantomData<&'b &'a ()> {
|
|
||||||
PhantomData
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(path_statements)]
|
|
||||||
fn caller<'b, 'a>() {
|
|
||||||
foo::<'b, 'a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In contrast to above, below code correctly does NOT compile.
|
|
||||||
// fn caller<'b, 'a>() {
|
|
||||||
// foo::<'b, 'a>();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// error: lifetime may not live long enough
|
|
||||||
// --> src/main.rs:22:5
|
|
||||||
// |
|
|
||||||
// 21 | fn caller<'b, 'a>() {
|
|
||||||
// | -- -- lifetime `'a` defined here
|
|
||||||
// | |
|
|
||||||
// | lifetime `'b` defined here
|
|
||||||
// 22 | foo::<'b, 'a>();
|
|
||||||
// | ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
|
|
||||||
// |
|
|
||||||
// = help: consider adding the following bound: `'a: 'b`
|
|
||||||
|
|
||||||
fn main() {}
|
|
44
tests/ui/wf/wf-fn-def-check-sig-1.rs
Normal file
44
tests/ui/wf/wf-fn-def-check-sig-1.rs
Normal file
@ -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
tests/ui/wf/wf-fn-def-check-sig-1.stderr
Normal file
14
tests/ui/wf/wf-fn-def-check-sig-1.stderr
Normal file
@ -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
tests/ui/wf/wf-fn-def-check-sig-2.rs
Normal file
44
tests/ui/wf/wf-fn-def-check-sig-2.rs
Normal file
@ -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
tests/ui/wf/wf-fn-def-check-sig-2.stderr
Normal file
15
tests/ui/wf/wf-fn-def-check-sig-2.stderr
Normal file
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user