Rollup merge of #119350 - fmease:lazy-ty-aliases-implied-bounds, r=compiler-errors
Imply outlives-bounds on lazy type aliases Fixes #118479. r? types
This commit is contained in:
commit
fc591dbc94
5 changed files with 211 additions and 97 deletions
|
@ -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>(
|
fn insert_required_predicates_to_be_wf<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
field_ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
field_span: Span,
|
span: Span,
|
||||||
global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
|
global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
|
||||||
required_predicates: &mut RequiredPredicates<'tcx>,
|
required_predicates: &mut RequiredPredicates<'tcx>,
|
||||||
explicit_map: &mut ExplicitPredicatesMap<'tcx>,
|
explicit_map: &mut ExplicitPredicatesMap<'tcx>,
|
||||||
) {
|
) {
|
||||||
for arg in field_ty.walk() {
|
for arg in ty.walk() {
|
||||||
let ty = match arg.unpack() {
|
let leaf_ty = match arg.unpack() {
|
||||||
GenericArgKind::Type(ty) => ty,
|
GenericArgKind::Type(ty) => ty,
|
||||||
|
|
||||||
// No predicates from lifetimes or constants, except potentially
|
// 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,
|
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
match *ty.kind() {
|
match *leaf_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
|
|
||||||
ty::Ref(region, rty, _) => {
|
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");
|
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) => {
|
ty::Adt(def, args) => {
|
||||||
// First check the inferred predicates
|
// For ADTs (structs/enums/unions), we check inferred and explicit 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`.
|
|
||||||
debug!("Adt");
|
debug!("Adt");
|
||||||
if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) {
|
check_inferred_predicates(
|
||||||
for (unsubstituted_predicate, &span) in
|
tcx,
|
||||||
unsubstituted_predicates.as_ref().skip_binder()
|
def.did(),
|
||||||
{
|
args,
|
||||||
// `unsubstituted_predicate` is `U: 'b` in the
|
global_inferred_outlives,
|
||||||
// example above. So apply the substitution to
|
required_predicates,
|
||||||
// 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_explicit_predicates(
|
check_explicit_predicates(
|
||||||
tcx,
|
tcx,
|
||||||
def.did(),
|
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, ..) => {
|
ty::Dynamic(obj, ..) => {
|
||||||
// This corresponds to `dyn Trait<..>`. In this case, we should
|
// This corresponds to `dyn Trait<..>`. In this case, we should
|
||||||
// use the explicit predicates as well.
|
// use the explicit predicates as well.
|
||||||
|
|
||||||
debug!("Dynamic");
|
debug!("Dynamic");
|
||||||
debug!("field_ty = {}", &field_ty);
|
|
||||||
debug!("ty in field = {}", &ty);
|
|
||||||
if let Some(ex_trait_ref) = obj.principal() {
|
if let Some(ex_trait_ref) = obj.principal() {
|
||||||
// Here, we are passing the type `usize` as a
|
// Here, we are passing the type `usize` as a
|
||||||
// placeholder value with the function
|
// placeholder value with the function
|
||||||
|
@ -198,21 +190,22 @@ fn insert_required_predicates_to_be_wf<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ty::Alias(ty::Projection, obj) => {
|
ty::Alias(ty::Projection, alias) => {
|
||||||
// This corresponds to `<T as Foo<'a>>::Bar`. In this case, we should use the
|
// This corresponds to a type like `<() as Trait<'a, T>>::Type`.
|
||||||
// explicit predicates as well.
|
// We only use the explicit predicates of the trait but
|
||||||
|
// not the ones of the associated type itself.
|
||||||
debug!("Projection");
|
debug!("Projection");
|
||||||
check_explicit_predicates(
|
check_explicit_predicates(
|
||||||
tcx,
|
tcx,
|
||||||
tcx.parent(obj.def_id),
|
tcx.parent(alias.def_id),
|
||||||
obj.args,
|
alias.args,
|
||||||
required_predicates,
|
required_predicates,
|
||||||
explicit_map,
|
explicit_map,
|
||||||
None,
|
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, _) => {}
|
ty::Alias(ty::Inherent, _) => {}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -220,19 +213,21 @@ fn insert_required_predicates_to_be_wf<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We also have to check the explicit predicates
|
/// Check the explicit predicates declared on the type.
|
||||||
/// declared on the type.
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
/// ```ignore (illustrative)
|
/// ```ignore (illustrative)
|
||||||
/// struct Foo<'a, T> {
|
/// struct Outer<'a, T> {
|
||||||
/// field1: Bar<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
|
/// 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`,
|
/// can ignore, but we will want to process `U: 'static`,
|
||||||
/// applying the substitution as above.
|
/// applying the substitution as above.
|
||||||
fn check_explicit_predicates<'tcx>(
|
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);
|
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(());
|
let crate_map = tcx.inferred_outlives_crate(());
|
||||||
crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
|
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 => {
|
DefKind::AnonConst if tcx.features().generic_const_exprs => {
|
||||||
let id = tcx.local_def_id_to_hir_id(item_def_id);
|
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() {
|
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<'_> {
|
fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
|
||||||
// Compute a map from each struct/enum/union S to the **explicit**
|
// Compute a map from each ADT (struct/enum/union) and lazy type alias to
|
||||||
// outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
|
// 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
|
// Typically there won't be many of these, except in older code where
|
||||||
// they were mandatory. Nonetheless, we have to ensure that every such
|
// they were mandatory. Nonetheless, we have to ensure that every such
|
||||||
// predicate is satisfied, so they form a kind of base set of requirements
|
// 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
|
| `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
|
error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
|
||||||
--> $DIR/duplicate.rs:11:36
|
--> $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`
|
= 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
|
error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
|
||||||
--> $DIR/duplicate.rs:243:34
|
--> $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…
Add table
Add a link
Reference in a new issue