diff options
| author | lcnr <rust@lcnr.de> | 2024-02-27 15:00:22 +0100 |
|---|---|---|
| committer | lcnr <rust@lcnr.de> | 2024-02-27 15:00:22 +0100 |
| commit | 93bc7a428cdf64a822274f7c5647ad46f8ecf122 (patch) | |
| tree | d56317f7493a07db050e294e2fc14a37b1c1768a | |
| parent | 91cae1dcdcf1a31bd8a92e4a63793d65cfe289bb (diff) | |
wf-check RPITs
10 files changed, 184 insertions, 34 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 3d9aa428c74..1a3aa95743c 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -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>( diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 3f9bd509b08..191671bcc1e 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -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 diff --git a/tests/ui/impl-trait/wf-check-hidden-type.rs b/tests/ui/impl-trait/wf-check-hidden-type.rs new file mode 100644 index 00000000000..29f15024d76 --- /dev/null +++ b/tests/ui/impl-trait/wf-check-hidden-type.rs @@ -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); +} diff --git a/tests/ui/impl-trait/wf-check-hidden-type.stderr b/tests/ui/impl-trait/wf-check-hidden-type.stderr new file mode 100644 index 00000000000..10c766c268c --- /dev/null +++ b/tests/ui/impl-trait/wf-check-hidden-type.stderr @@ -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`. diff --git a/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs b/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs new file mode 100644 index 00000000000..2b5c9781a96 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs @@ -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() {} diff --git a/tests/ui/type-alias-impl-trait/wf-check-definition-site.stderr b/tests/ui/type-alias-impl-trait/wf-check-definition-site.stderr new file mode 100644 index 00000000000..a51c6bb2ec0 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-check-definition-site.stderr @@ -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`. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr index 17c1f8897bf..dd6b5a166ec 100644 --- a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr +++ b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr @@ -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; | ^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr new file mode 100644 index 00000000000..820710afef5 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr @@ -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`. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr index f5d3a218542..2bc8be27a60 100644 --- a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr +++ b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr @@ -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(); | ^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/wf-nested.rs b/tests/ui/type-alias-impl-trait/wf-nested.rs index 1fc93a3cd27..dcb4e251d47 100644 --- a/tests/ui/type-alias-impl-trait/wf-nested.rs +++ b/tests/ui/type-alias-impl-trait/wf-nested.rs @@ -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. |
