about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-01-05 20:39:51 +0100
committerGitHub <noreply@github.com>2024-01-05 20:39:51 +0100
commitfc591dbc9446d96715c949f0a2218b9364d17bbf (patch)
tree1254ab65f764f1d663283f7a7615c25af1707f27
parent3a0536ab518cec6d866700e8314a74950e883fe0 (diff)
parent90d6fe2cba2b621b4fc331a1f6dd67c63a7c1033 (diff)
downloadrust-fc591dbc9446d96715c949f0a2218b9364d17bbf.tar.gz
rust-fc591dbc9446d96715c949f0a2218b9364d17bbf.zip
Rollup merge of #119350 - fmease:lazy-ty-aliases-implied-bounds, r=compiler-errors
Imply outlives-bounds on lazy type aliases

Fixes #118479.

r? types
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs179
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs8
-rw-r--r--tests/ui/associated-type-bounds/duplicate.stderr48
-rw-r--r--tests/ui/lazy-type-alias/implied-outlives-bounds.neg.stderr34
-rw-r--r--tests/ui/lazy-type-alias/implied-outlives-bounds.rs39
5 files changed, 211 insertions, 97 deletions
diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
index c17925471d9..0cb38094cec 100644
--- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
@@ -59,6 +59,17 @@ pub(super) fn infer_predicates(
                     }
                 }
 
+                DefKind::TyAlias if tcx.type_alias_is_lazy(item_did) => {
+                    insert_required_predicates_to_be_wf(
+                        tcx,
+                        tcx.type_of(item_did).instantiate_identity(),
+                        tcx.def_span(item_did),
+                        &global_inferred_outlives,
+                        &mut item_required_predicates,
+                        &mut explicit_map,
+                    );
+                }
+
                 _ => {}
             };
 
@@ -88,14 +99,14 @@ pub(super) fn infer_predicates(
 
 fn insert_required_predicates_to_be_wf<'tcx>(
     tcx: TyCtxt<'tcx>,
-    field_ty: Ty<'tcx>,
-    field_span: Span,
+    ty: Ty<'tcx>,
+    span: Span,
     global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
     required_predicates: &mut RequiredPredicates<'tcx>,
     explicit_map: &mut ExplicitPredicatesMap<'tcx>,
 ) {
-    for arg in field_ty.walk() {
-        let ty = match arg.unpack() {
+    for arg in ty.walk() {
+        let leaf_ty = match arg.unpack() {
             GenericArgKind::Type(ty) => ty,
 
             // No predicates from lifetimes or constants, except potentially
@@ -103,63 +114,26 @@ fn insert_required_predicates_to_be_wf<'tcx>(
             GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
         };
 
-        match *ty.kind() {
-            // The field is of type &'a T which means that we will have
-            // a predicate requirement of T: 'a (T outlives 'a).
-            //
-            // We also want to calculate potential predicates for the T
+        match *leaf_ty.kind() {
             ty::Ref(region, rty, _) => {
+                // The type is `&'a T` which means that we will have
+                // a predicate requirement of `T: 'a` (`T` outlives `'a`).
+                //
+                // We also want to calculate potential predicates for the `T`.
                 debug!("Ref");
-                insert_outlives_predicate(tcx, rty.into(), region, field_span, required_predicates);
+                insert_outlives_predicate(tcx, rty.into(), region, span, required_predicates);
             }
 
-            // For each Adt (struct/enum/union) type `Foo<'a, T>`, we
-            // can load the current set of inferred and explicit
-            // predicates from `global_inferred_outlives` and filter the
-            // ones that are TypeOutlives.
             ty::Adt(def, args) => {
-                // First check the inferred predicates
-                //
-                // Example 1:
-                //
-                //     struct Foo<'a, T> {
-                //         field1: Bar<'a, T>
-                //     }
-                //
-                //     struct Bar<'b, U> {
-                //         field2: &'b U
-                //     }
-                //
-                // Here, when processing the type of `field1`, we would
-                // request the set of implicit predicates computed for `Bar`
-                // thus far. This will initially come back empty, but in next
-                // round we will get `U: 'b`. We then apply the substitution
-                // `['b => 'a, U => T]` and thus get the requirement that `T:
-                // 'a` holds for `Foo`.
+                // For ADTs (structs/enums/unions), we check inferred and explicit predicates.
                 debug!("Adt");
-                if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) {
-                    for (unsubstituted_predicate, &span) in
-                        unsubstituted_predicates.as_ref().skip_binder()
-                    {
-                        // `unsubstituted_predicate` is `U: 'b` in the
-                        // example above. So apply the substitution to
-                        // get `T: 'a` (or `predicate`):
-                        let predicate = unsubstituted_predicates
-                            .rebind(*unsubstituted_predicate)
-                            .instantiate(tcx, args);
-                        insert_outlives_predicate(
-                            tcx,
-                            predicate.0,
-                            predicate.1,
-                            span,
-                            required_predicates,
-                        );
-                    }
-                }
-
-                // Check if the type has any explicit predicates that need
-                // to be added to `required_predicates`
-                // let _: () = args.region_at(0);
+                check_inferred_predicates(
+                    tcx,
+                    def.did(),
+                    args,
+                    global_inferred_outlives,
+                    required_predicates,
+                );
                 check_explicit_predicates(
                     tcx,
                     def.did(),
@@ -170,13 +144,31 @@ fn insert_required_predicates_to_be_wf<'tcx>(
                 );
             }
 
+            ty::Alias(ty::Weak, alias) => {
+                // This corresponds to a type like `Type<'a, T>`.
+                // We check inferred and explicit predicates.
+                debug!("Weak");
+                check_inferred_predicates(
+                    tcx,
+                    alias.def_id,
+                    alias.args,
+                    global_inferred_outlives,
+                    required_predicates,
+                );
+                check_explicit_predicates(
+                    tcx,
+                    alias.def_id,
+                    alias.args,
+                    required_predicates,
+                    explicit_map,
+                    None,
+                );
+            }
+
             ty::Dynamic(obj, ..) => {
                 // This corresponds to `dyn Trait<..>`. In this case, we should
                 // use the explicit predicates as well.
-
                 debug!("Dynamic");
-                debug!("field_ty = {}", &field_ty);
-                debug!("ty in field = {}", &ty);
                 if let Some(ex_trait_ref) = obj.principal() {
                     // Here, we are passing the type `usize` as a
                     // placeholder value with the function
@@ -198,21 +190,22 @@ fn insert_required_predicates_to_be_wf<'tcx>(
                 }
             }
 
-            ty::Alias(ty::Projection, obj) => {
-                // This corresponds to `<T as Foo<'a>>::Bar`. In this case, we should use the
-                // explicit predicates as well.
+            ty::Alias(ty::Projection, alias) => {
+                // This corresponds to a type like `<() as Trait<'a, T>>::Type`.
+                // We only use the explicit predicates of the trait but
+                // not the ones of the associated type itself.
                 debug!("Projection");
                 check_explicit_predicates(
                     tcx,
-                    tcx.parent(obj.def_id),
-                    obj.args,
+                    tcx.parent(alias.def_id),
+                    alias.args,
                     required_predicates,
                     explicit_map,
                     None,
                 );
             }
 
-            // FIXME(inherent_associated_types): Handle this case properly.
+            // FIXME(inherent_associated_types): Use the explicit predicates from the parent impl.
             ty::Alias(ty::Inherent, _) => {}
 
             _ => {}
@@ -220,19 +213,21 @@ fn insert_required_predicates_to_be_wf<'tcx>(
     }
 }
 
-/// We also have to check the explicit predicates
-/// declared on the type.
+/// Check the explicit predicates declared on the type.
+///
+/// ### Example
+///
 /// ```ignore (illustrative)
-/// struct Foo<'a, T> {
-///     field1: Bar<T>
+/// struct Outer<'a, T> {
+///     field: Inner<T>,
 /// }
 ///
-/// struct Bar<U> where U: 'static, U: Foo {
-///     ...
+/// struct Inner<U> where U: 'static, U: Outer {
+///     // ...
 /// }
 /// ```
 /// Here, we should fetch the explicit predicates, which
-/// will give us `U: 'static` and `U: Foo`. The latter we
+/// will give us `U: 'static` and `U: Outer`. The latter we
 /// can ignore, but we will want to process `U: 'static`,
 /// applying the substitution as above.
 fn check_explicit_predicates<'tcx>(
@@ -303,3 +298,45 @@ fn check_explicit_predicates<'tcx>(
         insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates);
     }
 }
+
+/// Check the inferred predicates declared on the type.
+///
+/// ### Example
+///
+/// ```ignore (illustrative)
+/// struct Outer<'a, T> {
+///     outer: Inner<'a, T>,
+/// }
+///
+/// struct Inner<'b, U> {
+///     inner: &'b U,
+/// }
+/// ```
+///
+/// Here, when processing the type of field `outer`, we would request the
+/// set of implicit predicates computed for `Inner` thus far. This will
+/// initially come back empty, but in next round we will get `U: 'b`.
+/// We then apply the substitution `['b => 'a, U => T]` and thus get the
+/// requirement that `T: 'a` holds for `Outer`.
+fn check_inferred_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    args: ty::GenericArgsRef<'tcx>,
+    global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
+    required_predicates: &mut RequiredPredicates<'tcx>,
+) {
+    // Load the current set of inferred and explicit predicates from `global_inferred_outlives`
+    // and filter the ones that are `TypeOutlives`.
+
+    let Some(predicates) = global_inferred_outlives.get(&def_id) else {
+        return;
+    };
+
+    for (&predicate, &span) in predicates.as_ref().skip_binder() {
+        // `predicate` is `U: 'b` in the example above.
+        // So apply the substitution to get `T: 'a`.
+        let ty::OutlivesPredicate(arg, region) =
+            predicates.rebind(predicate).instantiate(tcx, args);
+        insert_outlives_predicate(tcx, arg, region, span, required_predicates);
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index 72511bfa01f..a87112dcc12 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -21,6 +21,10 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
             let crate_map = tcx.inferred_outlives_crate(());
             crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
         }
+        DefKind::TyAlias if tcx.type_alias_is_lazy(item_def_id) => {
+            let crate_map = tcx.inferred_outlives_crate(());
+            crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
+        }
         DefKind::AnonConst if tcx.features().generic_const_exprs => {
             let id = tcx.local_def_id_to_hir_id(item_def_id);
             if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
@@ -47,8 +51,8 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
 }
 
 fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
-    // Compute a map from each struct/enum/union S to the **explicit**
-    // outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
+    // Compute a map from each ADT (struct/enum/union) and lazy type alias to
+    // the **explicit** outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
     // Typically there won't be many of these, except in older code where
     // they were mandatory. Nonetheless, we have to ensure that every such
     // predicate is satisfied, so they form a kind of base set of requirements
diff --git a/tests/ui/associated-type-bounds/duplicate.stderr b/tests/ui/associated-type-bounds/duplicate.stderr
index 3888e62230f..38b812dfdd4 100644
--- a/tests/ui/associated-type-bounds/duplicate.stderr
+++ b/tests/ui/associated-type-bounds/duplicate.stderr
@@ -7,6 +7,30 @@ LL | struct SI1<T: Iterator<Item: Copy, Item: Send>> {
    |                        `Item` bound here first
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
+  --> $DIR/duplicate.rs:255:40
+   |
+LL | type TADyn1 = dyn Iterator<Item: Copy, Item: Send>;
+   |                            ----------  ^^^^^^^^^^ re-bound here
+   |                            |
+   |                            `Item` bound here first
+
+error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
+  --> $DIR/duplicate.rs:257:44
+   |
+LL | type TADyn2 = Box<dyn Iterator<Item: Copy, Item: Copy>>;
+   |                                ----------  ^^^^^^^^^^ re-bound here
+   |                                |
+   |                                `Item` bound here first
+
+error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
+  --> $DIR/duplicate.rs:259:43
+   |
+LL | type TADyn3 = dyn Iterator<Item: 'static, Item: 'static>;
+   |                            -------------  ^^^^^^^^^^^^^ re-bound here
+   |                            |
+   |                            `Item` bound here first
+
+error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
   --> $DIR/duplicate.rs:11:36
    |
 LL | struct SI2<T: Iterator<Item: Copy, Item: Copy>> {
@@ -491,30 +515,6 @@ LL |     Self: Iterator<Item: 'static, Item: 'static>,
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:255:40
-   |
-LL | type TADyn1 = dyn Iterator<Item: Copy, Item: Send>;
-   |                            ----------  ^^^^^^^^^^ re-bound here
-   |                            |
-   |                            `Item` bound here first
-
-error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:257:44
-   |
-LL | type TADyn2 = Box<dyn Iterator<Item: Copy, Item: Copy>>;
-   |                                ----------  ^^^^^^^^^^ re-bound here
-   |                                |
-   |                                `Item` bound here first
-
-error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
-  --> $DIR/duplicate.rs:259:43
-   |
-LL | type TADyn3 = dyn Iterator<Item: 'static, Item: 'static>;
-   |                            -------------  ^^^^^^^^^^^^^ re-bound here
-   |                            |
-   |                            `Item` bound here first
-
-error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
   --> $DIR/duplicate.rs:243:34
    |
 LL |     type A: Iterator<Item: Copy, Item: Send>;
diff --git a/tests/ui/lazy-type-alias/implied-outlives-bounds.neg.stderr b/tests/ui/lazy-type-alias/implied-outlives-bounds.neg.stderr
new file mode 100644
index 00000000000..a2dbb58ecd0
--- /dev/null
+++ b/tests/ui/lazy-type-alias/implied-outlives-bounds.neg.stderr
@@ -0,0 +1,34 @@
+error: lifetime may not live long enough
+  --> $DIR/implied-outlives-bounds.rs:21:12
+   |
+LL | fn env0<'any>() {
+   |         ---- lifetime `'any` defined here
+LL |     let _: TypeOutlives<'static, &'any ()>;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/implied-outlives-bounds.rs:26:12
+   |
+LL | fn env1<'any>() {
+   |         ---- lifetime `'any` defined here
+LL |     let _: RegionOutlives<'static, 'any>;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/implied-outlives-bounds.rs:31:12
+   |
+LL | fn env2<'any>() {
+   |         ---- lifetime `'any` defined here
+LL |     let _: Outer0<'static, &'any ()>;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/implied-outlives-bounds.rs:36:12
+   |
+LL | fn env3<'any>() {
+   |         ---- lifetime `'any` defined here
+LL |     let _: Outer1<'static, &'any ()>;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/lazy-type-alias/implied-outlives-bounds.rs b/tests/ui/lazy-type-alias/implied-outlives-bounds.rs
new file mode 100644
index 00000000000..c08e45975de
--- /dev/null
+++ b/tests/ui/lazy-type-alias/implied-outlives-bounds.rs
@@ -0,0 +1,39 @@
+// Check that we imply outlives-bounds on lazy type aliases.
+
+// revisions: pos neg
+//[pos] check-pass
+
+#![feature(lazy_type_alias)]
+#![allow(incomplete_features)]
+
+type TypeOutlives<'a, T> = &'a T;
+type RegionOutlives<'a, 'b> = &'a &'b ();
+
+// Ensure that we imply bounds from the explicit bounds of weak aliases.
+struct Outer0<'a, T>(ExplicitTypeOutlives<'a, T>);
+type ExplicitTypeOutlives<'a, T: 'a> = (&'a (), T);
+
+// Ensure that we imply bounds from the implied bounds of weak aliases.
+type Outer1<'b, U> = TypeOutlives<'b, U>;
+
+#[cfg(neg)]
+fn env0<'any>() {
+    let _: TypeOutlives<'static, &'any ()>; //[neg]~ ERROR lifetime may not live long enough
+}
+
+#[cfg(neg)]
+fn env1<'any>() {
+    let _: RegionOutlives<'static, 'any>; //[neg]~ ERROR lifetime may not live long enough
+}
+
+#[cfg(neg)]
+fn env2<'any>() {
+    let _: Outer0<'static, &'any ()>; //[neg]~ ERROR lifetime may not live long enough
+}
+
+#[cfg(neg)]
+fn env3<'any>() {
+    let _: Outer1<'static, &'any ()>; //[neg]~ ERROR lifetime may not live long enough
+}
+
+fn main() {}