mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Check unnormalized signature on pointer cast
This commit is contained in:
parent
eb33b43bab
commit
e8472e84e3
@ -1979,19 +1979,76 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
|
|
||||||
match cast_kind {
|
match cast_kind {
|
||||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
|
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
|
||||||
let fn_sig = op.ty(body, tcx).fn_sig(tcx);
|
let src_sig = op.ty(body, tcx).fn_sig(tcx);
|
||||||
|
|
||||||
|
// HACK: This shouldn't be necessary... We can remove this when we actually
|
||||||
|
// get binders with where clauses, then elaborate implied bounds into that
|
||||||
|
// binder, and implement a higher-ranked subtyping algorithm that actually
|
||||||
|
// respects these implied bounds.
|
||||||
|
//
|
||||||
|
// This protects against the case where we are casting from a higher-ranked
|
||||||
|
// fn item to a non-higher-ranked fn pointer, where the cast throws away
|
||||||
|
// implied bounds that would've needed to be checked at the call site. This
|
||||||
|
// only works when we're casting to a non-higher-ranked fn ptr, since
|
||||||
|
// placeholders in the target signature could have untracked implied
|
||||||
|
// bounds, resulting in incorrect errors.
|
||||||
|
//
|
||||||
|
// We check that this signature is WF before subtyping the signature with
|
||||||
|
// the target fn sig.
|
||||||
|
if src_sig.has_bound_regions()
|
||||||
|
&& let ty::FnPtr(target_fn_tys, target_hdr) = *ty.kind()
|
||||||
|
&& let target_sig = target_fn_tys.with(target_hdr)
|
||||||
|
&& let Some(target_sig) = target_sig.no_bound_vars()
|
||||||
|
{
|
||||||
|
let src_sig = self.infcx.instantiate_binder_with_fresh_vars(
|
||||||
|
span,
|
||||||
|
BoundRegionConversionTime::HigherRankedType,
|
||||||
|
src_sig,
|
||||||
|
);
|
||||||
|
let src_ty = Ty::new_fn_ptr(self.tcx(), ty::Binder::dummy(src_sig));
|
||||||
|
self.prove_predicate(
|
||||||
|
ty::ClauseKind::WellFormed(src_ty.into()),
|
||||||
|
location.to_locations(),
|
||||||
|
ConstraintCategory::Cast { unsize_to: None },
|
||||||
|
);
|
||||||
|
|
||||||
|
let src_ty = self.normalize(src_ty, location);
|
||||||
|
if let Err(terr) = self.sub_types(
|
||||||
|
src_ty,
|
||||||
|
*ty,
|
||||||
|
location.to_locations(),
|
||||||
|
ConstraintCategory::Cast { unsize_to: None },
|
||||||
|
) {
|
||||||
|
span_mirbug!(
|
||||||
|
self,
|
||||||
|
rvalue,
|
||||||
|
"equating {:?} with {:?} yields {:?}",
|
||||||
|
target_sig,
|
||||||
|
src_sig,
|
||||||
|
terr
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let src_ty = Ty::new_fn_ptr(tcx, src_sig);
|
||||||
|
// HACK: We want to assert that the signature of the source fn is
|
||||||
|
// well-formed, because we don't enforce that via the WF of FnDef
|
||||||
|
// types normally. This should be removed when we improve the tracking
|
||||||
|
// of implied bounds of fn signatures.
|
||||||
|
self.prove_predicate(
|
||||||
|
ty::ClauseKind::WellFormed(src_ty.into()),
|
||||||
|
location.to_locations(),
|
||||||
|
ConstraintCategory::Cast { unsize_to: None },
|
||||||
|
);
|
||||||
|
|
||||||
// The type that we see in the fcx is like
|
// The type that we see in the fcx is like
|
||||||
// `foo::<'a, 'b>`, where `foo` is the path to a
|
// `foo::<'a, 'b>`, where `foo` is the path to a
|
||||||
// function definition. When we extract the
|
// function definition. When we extract the
|
||||||
// signature, it comes from the `fn_sig` query,
|
// signature, it comes from the `fn_sig` query,
|
||||||
// and hence may contain unnormalized results.
|
// and hence may contain unnormalized results.
|
||||||
let fn_sig = self.normalize(fn_sig, location);
|
let src_ty = self.normalize(src_ty, location);
|
||||||
|
|
||||||
let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);
|
|
||||||
|
|
||||||
if let Err(terr) = self.sub_types(
|
if let Err(terr) = self.sub_types(
|
||||||
ty_fn_ptr_from,
|
src_ty,
|
||||||
*ty,
|
*ty,
|
||||||
location.to_locations(),
|
location.to_locations(),
|
||||||
ConstraintCategory::Cast { unsize_to: None },
|
ConstraintCategory::Cast { unsize_to: None },
|
||||||
@ -2000,7 +2057,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||||||
self,
|
self,
|
||||||
rvalue,
|
rvalue,
|
||||||
"equating {:?} with {:?} yields {:?}",
|
"equating {:?} with {:?} yields {:?}",
|
||||||
ty_fn_ptr_from,
|
src_ty,
|
||||||
ty,
|
ty,
|
||||||
terr
|
terr
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
static UNIT: &'static &'static () = &&();
|
||||||
|
|
||||||
|
fn foo<'a: 'a, 'b: 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }
|
||||||
|
|
||||||
|
fn bad<'a, T>(x: &'a T) -> &'static T {
|
||||||
|
let f: fn(_, &'a T) -> &'static T = foo;
|
||||||
|
//~^ ERROR lifetime may not live long enough
|
||||||
|
f(UNIT, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,10 @@
|
|||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/implied-bounds-on-nested-references-plus-variance-early-bound.rs:6:12
|
||||||
|
|
|
||||||
|
LL | fn bad<'a, T>(x: &'a T) -> &'static T {
|
||||||
|
| -- lifetime `'a` defined here
|
||||||
|
LL | let f: fn(_, &'a T) -> &'static T = foo;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
@ -0,0 +1,17 @@
|
|||||||
|
trait ToArg<T> {
|
||||||
|
type Arg;
|
||||||
|
}
|
||||||
|
impl<T, U> ToArg<T> for U {
|
||||||
|
type Arg = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extend_inner<'a, 'b>(x: &'a str) -> <&'b &'a () as ToArg<&'b str>>::Arg { x }
|
||||||
|
fn extend<'a, 'b>(x: &'a str) -> &'b str {
|
||||||
|
(extend_inner as fn(_) -> _)(x)
|
||||||
|
//~^ ERROR lifetime may not live long enough
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let y = extend(&String::from("Hello World"));
|
||||||
|
println!("{}", y);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/implied-bounds-on-nested-references-plus-variance-unnormalized.rs:10:5
|
||||||
|
|
|
||||||
|
LL | fn extend<'a, 'b>(x: &'a str) -> &'b str {
|
||||||
|
| -- -- lifetime `'b` defined here
|
||||||
|
| |
|
||||||
|
| lifetime `'a` defined here
|
||||||
|
LL | (extend_inner as fn(_) -> _)(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 1 previous error
|
||||||
|
|
@ -1,15 +1,10 @@
|
|||||||
//@ check-pass
|
|
||||||
//@ known-bug: #25860
|
|
||||||
|
|
||||||
// Should fail. The combination of variance and implied bounds for nested
|
|
||||||
// references allows us to infer a longer lifetime than we can prove.
|
|
||||||
|
|
||||||
static UNIT: &'static &'static () = &&();
|
static UNIT: &'static &'static () = &&();
|
||||||
|
|
||||||
fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }
|
fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }
|
||||||
|
|
||||||
fn bad<'a, T>(x: &'a T) -> &'static T {
|
fn bad<'a, T>(x: &'a T) -> &'static T {
|
||||||
let f: fn(_, &'a T) -> &'static T = foo;
|
let f: fn(_, &'a T) -> &'static T = foo;
|
||||||
|
//~^ ERROR lifetime may not live long enough
|
||||||
f(UNIT, x)
|
f(UNIT, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/implied-bounds-on-nested-references-plus-variance.rs:6:12
|
||||||
|
|
|
||||||
|
LL | fn bad<'a, T>(x: &'a T) -> &'static T {
|
||||||
|
| -- lifetime `'a` defined here
|
||||||
|
LL | let f: fn(_, &'a T) -> &'static T = foo;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
Loading…
Reference in New Issue
Block a user