Skip to content

Commit

Permalink
Rollup merge of #119350 - fmease:lazy-ty-aliases-implied-bounds, r=co…
Browse files Browse the repository at this point in the history
…mpiler-errors

Imply outlives-bounds on lazy type aliases

Fixes #118479.

r? types
  • Loading branch information
matthiaskrgr authored Jan 5, 2024
2 parents 3a0536a + 90d6fe2 commit fc591db
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 97 deletions.
179 changes: 108 additions & 71 deletions compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}

_ => {}
};

Expand Down Expand Up @@ -88,78 +99,41 @@ 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
// constants' types, but `walk` will get to them as well.
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(),
Expand All @@ -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
Expand All @@ -198,41 +190,44 @@ 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, _) => {}

_ => {}
}
}
}

/// 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>(
Expand Down Expand Up @@ -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);
}
}
8 changes: 6 additions & 2 deletions compiler/rustc_hir_analysis/src/outlives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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
Expand Down
48 changes: 24 additions & 24 deletions tests/ui/associated-type-bounds/duplicate.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,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
|
Expand Down Expand Up @@ -490,30 +514,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
|
Expand Down
34 changes: 34 additions & 0 deletions tests/ui/lazy-type-alias/implied-outlives-bounds.neg.stderr
Original file line number Diff line number Diff line change
@@ -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

Loading

0 comments on commit fc591db

Please sign in to comment.