From 90d6fe2cba2b621b4fc331a1f6dd67c63a7c1033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 27 Dec 2023 15:29:52 +0100 Subject: [PATCH] Imply outlives-bounds on lazy type aliases --- .../src/outlives/implicit_infer.rs | 179 +++++++++++------- .../rustc_hir_analysis/src/outlives/mod.rs | 8 +- .../associated-type-bounds/duplicate.stderr | 48 ++--- .../implied-outlives-bounds.neg.stderr | 34 ++++ .../implied-outlives-bounds.rs | 39 ++++ 5 files changed, 211 insertions(+), 97 deletions(-) create mode 100644 tests/ui/lazy-type-alias/implied-outlives-bounds.neg.stderr create mode 100644 tests/ui/lazy-type-alias/implied-outlives-bounds.rs diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index c17925471d983..0cb38094ceca5 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>>, 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 `>::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 +/// struct Outer<'a, T> { +/// field: Inner, /// } /// -/// struct Bar where U: 'static, U: Foo { -/// ... +/// struct Inner 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>>, + 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 72511bfa01f14..a87112dcc1230 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 3888e62230f14..38b812dfdd442 100644 --- a/tests/ui/associated-type-bounds/duplicate.stderr +++ b/tests/ui/associated-type-bounds/duplicate.stderr @@ -6,6 +6,30 @@ LL | struct SI1> { | | | `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; + | ---------- ^^^^^^^^^^ 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>; + | ---------- ^^^^^^^^^^ 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; + | ------------- ^^^^^^^^^^^^^ 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 | @@ -490,30 +514,6 @@ LL | Self: Iterator, | = 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; - | ---------- ^^^^^^^^^^ 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>; - | ---------- ^^^^^^^^^^ 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; - | ------------- ^^^^^^^^^^^^^ 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 | 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 0000000000000..a2dbb58ecd03d --- /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 0000000000000..c08e45975de10 --- /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() {}