wf-check RPITs

This commit is contained in:
lcnr 2024-02-27 15:00:22 +01:00
parent 91cae1dcdc
commit 93bc7a428c
10 changed files with 184 additions and 34 deletions

View File

@ -403,16 +403,6 @@ fn check_opaque_meets_bounds<'tcx>(
return Err(guar);
}
match origin {
// Checked when type checking the function containing them.
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
// HACK: this should also fall through to the hidden type check below, but the original
// implementation had a bug where equivalent lifetimes are not identical. This caused us
// to reject existing stable code that is otherwise completely fine. The real fix is to
// compare the hidden types via our type equivalence/relation infra instead of doing an
// identity check.
let _ = infcx.take_opaque_types();
return Ok(());
}
// Nested opaque types occur only in associated types:
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
// They can only be referenced as `<Opaque<T> as Trait<&'static T>>::AssocTy`.
@ -421,20 +411,33 @@ fn check_opaque_meets_bounds<'tcx>(
hir::OpaqueTyOrigin::TyAlias { .. }
if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
// Can have different predicates to their defining use
hir::OpaqueTyOrigin::TyAlias { .. } => {
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
hir::OpaqueTyOrigin::TyAlias { .. }
| hir::OpaqueTyOrigin::FnReturn(..)
| hir::OpaqueTyOrigin::AsyncFn(..) => {
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?;
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
}
}
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
for (mut key, mut ty) in infcx.take_opaque_types() {
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
key = infcx.resolve_vars_if_possible(key);
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
// HACK: this should also fall through to the hidden type check below, but the original
// implementation had a bug where equivalent lifetimes are not identical. This caused us
// to reject existing stable code that is otherwise completely fine. The real fix is to
// compare the hidden types via our type equivalence/relation infra instead of doing an
// identity check.
let _ = infcx.take_opaque_types();
Ok(())
} else {
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
for (mut key, mut ty) in infcx.take_opaque_types() {
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
key = infcx.resolve_vars_if_possible(key);
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
}
Ok(())
}
Ok(())
}
fn sanity_check_found_hidden_type<'tcx>(

View File

@ -121,18 +121,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
}
}
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) {
DefKind::TyAlias => ty::List::empty(),
DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
// Nested opaque types only occur in associated types:
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
// assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself
// and `&'static T`.
DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"),
def_kind => {
bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
}
},
DefKind::OpaqueTy => bug!("implied bounds are not defined for opaques"),
DefKind::Mod
| DefKind::Struct
| DefKind::Union

View File

@ -0,0 +1,21 @@
//! Regression test for #114728.
trait Extend<'a, 'b> {
fn extend(self, _: &'a str) -> &'b str;
}
impl<'a, 'b> Extend<'a, 'b> for Option<&'b &'a ()> {
fn extend(self, s: &'a str) -> &'b str {
s
}
}
fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
//~^ ERROR in type `&'b &'a ()`, reference has a longer lifetime than the data it references
None::<&'_ &'_ ()>
}
fn main() {
let y = boom().extend(&String::from("temporary"));
println!("{}", y);
}

View File

@ -0,0 +1,20 @@
error[E0491]: in type `&'b &'a ()`, reference has a longer lifetime than the data it references
--> $DIR/wf-check-hidden-type.rs:13:22
|
LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
| ^^^^^^^^^^^^^^^^^^^
|
note: the pointer is valid for the lifetime `'b` as defined here
--> $DIR/wf-check-hidden-type.rs:13:13
|
LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
| ^^
note: but the referenced data is only valid for the lifetime `'a` as defined here
--> $DIR/wf-check-hidden-type.rs:13:9
|
LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
| ^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0491`.

View File

@ -0,0 +1,37 @@
// Regression test for #114572, We were inferring an ill-formed type:
//
// `Opaque<'a> = Static<&'a str>`, vs
// `Opaque<'a> = Static<&'static str>`.
#![feature(type_alias_impl_trait)]
struct Static<T: 'static>(T);
type OpaqueRet<'a> = impl Sized + 'a;
//~^ ERROR the type `&'a u8` does not fulfill the required lifetime
fn test_return<'a>(msg: Static<&'static u8>) -> OpaqueRet<'a> {
msg
}
fn test_rpit<'a>(msg: Static<&'static u8>) -> impl Sized + 'a {
//~^ ERROR the type `&'a u8` does not fulfill the required lifetime
msg
}
type OpaqueAssign<'a> = impl Sized + 'a;
//~^ ERROR the type `&'a u8` does not fulfill the required lifetime
fn test_assign<'a>(msg: Static<&'static u8>) -> Option<OpaqueAssign<'a>> {
let _: OpaqueAssign<'a> = msg;
None
}
// `OpaqueRef<'a, T> = Ref<'a, T>`, vs
// `OpaqueRef<'a, T> = Ref<'static, T>`.
trait RefAt<'a>: 'a {}
struct Ref<'a, T: RefAt<'a>>(&'a T);
type OpaqueRef<'a, T: RefAt<'static>> = impl Sized + 'a;
//~^ ERROR mismatched types
fn test_trait<'a, T: RefAt<'static>>(msg: Ref<'static, T>) -> OpaqueRef<'a, T> {
msg
}
fn main() {}

View File

@ -0,0 +1,55 @@
error[E0477]: the type `&'a u8` does not fulfill the required lifetime
--> $DIR/wf-check-definition-site.rs:9:22
|
LL | type OpaqueRet<'a> = impl Sized + 'a;
| ^^^^^^^^^^^^^^^
|
note: type must satisfy the static lifetime as required by this binding
--> $DIR/wf-check-definition-site.rs:7:18
|
LL | struct Static<T: 'static>(T);
| ^^^^^^^
error[E0477]: the type `&'a u8` does not fulfill the required lifetime
--> $DIR/wf-check-definition-site.rs:15:47
|
LL | fn test_rpit<'a>(msg: Static<&'static u8>) -> impl Sized + 'a {
| ^^^^^^^^^^^^^^^
|
note: type must satisfy the static lifetime as required by this binding
--> $DIR/wf-check-definition-site.rs:7:18
|
LL | struct Static<T: 'static>(T);
| ^^^^^^^
error[E0477]: the type `&'a u8` does not fulfill the required lifetime
--> $DIR/wf-check-definition-site.rs:20:25
|
LL | type OpaqueAssign<'a> = impl Sized + 'a;
| ^^^^^^^^^^^^^^^
|
note: type must satisfy the static lifetime as required by this binding
--> $DIR/wf-check-definition-site.rs:7:18
|
LL | struct Static<T: 'static>(T);
| ^^^^^^^
error[E0308]: mismatched types
--> $DIR/wf-check-definition-site.rs:31:41
|
LL | type OpaqueRef<'a, T: RefAt<'static>> = impl Sized + 'a;
| ^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected trait `RefAt<'a>`
found trait `RefAt<'static>`
note: the lifetime `'a` as defined here...
--> $DIR/wf-check-definition-site.rs:31:16
|
LL | type OpaqueRef<'a, T: RefAt<'static>> = impl Sized + 'a;
| ^^
= note: ...does not necessarily outlive the static lifetime
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0308, E0477.
For more information about an error, try `rustc --explain E0308`.

View File

@ -1,5 +1,5 @@
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/wf-nested.rs:57:27
--> $DIR/wf-nested.rs:60:27
|
LL | type InnerOpaque<T> = impl Sized;
| ^^^^^^^^^^

View File

@ -0,0 +1,22 @@
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/wf-nested.rs:36:57
|
LL | fn define_rpit<T>() -> impl Trait<&'static T, Out = impl Sized> {}
| ^^^^^^^^^^
| |
| the parameter type `T` must be valid for the static lifetime...
| ...so that the type `T` will meet its required lifetime bounds...
|
note: ...that is required by this bound
--> $DIR/wf-nested.rs:12:20
|
LL | struct IsStatic<T: 'static>(T);
| ^^^^^^^
help: consider adding an explicit lifetime bound
|
LL | fn define_rpit<T: 'static>() -> impl Trait<&'static T, Out = impl Sized> {}
| +++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0310`.

View File

@ -1,5 +1,5 @@
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/wf-nested.rs:46:17
--> $DIR/wf-nested.rs:49:17
|
LL | let _ = outer.get();
| ^^^^^^^^^^^
@ -13,7 +13,7 @@ LL | fn test<T: 'static>() {
| +++++++++
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/wf-nested.rs:46:17
--> $DIR/wf-nested.rs:49:17
|
LL | let _ = outer.get();
| ^^^^^^^^^^^

View File

@ -3,7 +3,7 @@
// See the comments below.
//
//@ revisions: pass pass_sound fail
//@ [pass] check-pass
//@ [pass] check-fail
//@ [pass_sound] check-fail
//@ [fail] check-fail
@ -32,6 +32,9 @@ mod pass {
use super::*;
type OuterOpaque<T> = impl Trait<&'static T, Out = impl Sized>;
fn define<T>() -> OuterOpaque<T> {}
fn define_rpit<T>() -> impl Trait<&'static T, Out = impl Sized> {}
//[pass]~^ ERROR the parameter type `T` may not live long enough
}
// Test the soundness of `pass` - We should require `T: 'static` at the use site.