Imply outlives-bounds on lazy type aliases
This commit is contained in:
parent
2fe50cd72c
commit
90d6fe2cba
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
||||
@ -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
|
||||
|
|
||||
|
34
tests/ui/lazy-type-alias/implied-outlives-bounds.neg.stderr
Normal file
34
tests/ui/lazy-type-alias/implied-outlives-bounds.neg.stderr
Normal file
@ -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
|
||||
|
39
tests/ui/lazy-type-alias/implied-outlives-bounds.rs
Normal file
39
tests/ui/lazy-type-alias/implied-outlives-bounds.rs
Normal file
@ -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() {}
|
Loading…
Reference in New Issue
Block a user