about summary refs log tree commit diff
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2024-02-27 15:00:22 +0100
committerlcnr <rust@lcnr.de>2024-02-27 15:00:22 +0100
commit93bc7a428cdf64a822274f7c5647ad46f8ecf122 (patch)
treed56317f7493a07db050e294e2fc14a37b1c1768a
parent91cae1dcdcf1a31bd8a92e4a63793d65cfe289bb (diff)
wf-check RPITs
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs39
-rw-r--r--compiler/rustc_ty_utils/src/implied_bounds.rs13
-rw-r--r--tests/ui/impl-trait/wf-check-hidden-type.rs21
-rw-r--r--tests/ui/impl-trait/wf-check-hidden-type.stderr20
-rw-r--r--tests/ui/type-alias-impl-trait/wf-check-definition-site.rs37
-rw-r--r--tests/ui/type-alias-impl-trait/wf-check-definition-site.stderr55
-rw-r--r--tests/ui/type-alias-impl-trait/wf-nested.fail.stderr2
-rw-r--r--tests/ui/type-alias-impl-trait/wf-nested.pass.stderr22
-rw-r--r--tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr4
-rw-r--r--tests/ui/type-alias-impl-trait/wf-nested.rs5
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.