Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Imply outlives-bounds on lazy type aliases #119350

Merged
merged 1 commit into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reordering of diagnostics prolly because some queries now get called earlier than later.

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
Loading