diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index 4f35b87be3016..ebf4fa7c8a82d 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -8,18 +8,13 @@ use tracing::debug; use super::explicit::ExplicitPredicatesMap; use super::utils::*; -/// Infer predicates for the items in the crate. -/// -/// `global_inferred_outlives`: this is initially the empty map that -/// was generated by walking the items in the crate. This will -/// now be filled with inferred predicates. +/// Infer outlives-predicates for the items in the local crate. pub(super) fn infer_predicates( tcx: TyCtxt<'_>, ) -> FxIndexMap>> { debug!("infer_predicates"); let mut explicit_map = ExplicitPredicatesMap::new(); - let mut global_inferred_outlives = FxIndexMap::default(); // If new predicates were added then we need to re-calculate @@ -70,6 +65,17 @@ pub(super) fn infer_predicates( ); } + DefKind::Const => { + 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, + ); + } + _ => {} }; @@ -312,7 +318,7 @@ fn check_explicit_predicates<'tcx>( } } -/// Check the inferred predicates declared on the type. +/// Check the inferred predicates of the type. /// /// ### Example /// diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index 499f5572f470a..296e4e9bc1136 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -15,17 +15,11 @@ pub(crate) fn provide(providers: &mut Providers) { fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clause<'_>, Span)] { match tcx.def_kind(item_def_id) { - DefKind::Struct | DefKind::Enum | DefKind::Union => { - 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::Struct | DefKind::Enum | DefKind::Union | DefKind::Const => {} + DefKind::TyAlias if tcx.type_alias_is_lazy(item_def_id) => {} 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() { + return if tcx.hir_opt_const_param_default_param_def_id(id).is_some() { // In `generics_of` we set the generics' parent to be our parent's parent which means that // we lose out on the predicates of our actual parent if we dont return those predicates here. // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) @@ -42,21 +36,22 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau tcx.inferred_outlives_of(item_def_id) } else { &[] - } + }; } - _ => &[], + _ => return &[], + } + + // FIXME(fmease): Explainer. Maybe fast path: + if tcx.generics_of(item_def_id).is_empty() { + return &[]; } + + let crate_map = tcx.inferred_outlives_crate(()); + crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]) } +/// Compute a map from each applicable item in the local crate to its inferred/implied outlives-predicates. fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { - // 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 - // for the type. - - // Compute the inferred predicates let global_inferred_outlives = implicit_infer::infer_predicates(tcx); // Convert the inferred predicates into the "collected" form the diff --git a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md index cdcb90d3e2edf..4b8be3663f99e 100644 --- a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md +++ b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md @@ -9,9 +9,9 @@ handled... well... implicitly. ## explicit implied bounds -The explicit implied bounds are computed in [`fn inferred_outlives_of`]. Only ADTs and -lazy type aliases have explicit implied bounds which are computed via a fixpoint algorithm -in the [`fn inferred_outlives_crate`] query. +The explicit implied bounds are computed in [`fn inferred_outlives_of`]. Only ADTs, +[lazy type aliases][lta] and [free (generic) const items][gci] have explicit implied bounds +which are computed via a fixpoint algorithm in the [`fn inferred_outlives_crate`] query. We use [`fn insert_required_predicates_to_be_wf`] on all fields of all ADTs in the crate. This function computes the outlives bounds for each component of the field using a @@ -31,6 +31,8 @@ if the outlived region is a region parameter. [It does not add `'static` require [`fn check_explicit_predicates`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs#L238 [`fn insert_outlives_predicate`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/utils.rs#L15 [nostatic]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/utils.rs#L159-L165 + [lta]: https://github.com/rust-lang/rust/issues/112792 + [gci]: https://github.com/rust-lang/rust/issues/113521 ## implicit implied bounds diff --git a/tests/ui/generic-const-items/implied-outlives-bounds.neg.stderr b/tests/ui/generic-const-items/implied-outlives-bounds.neg.stderr new file mode 100644 index 0000000000000..de458bd8bbe1d --- /dev/null +++ b/tests/ui/generic-const-items/implied-outlives-bounds.neg.stderr @@ -0,0 +1,18 @@ +error: lifetime may not live long enough + --> $DIR/implied-outlives-bounds.rs:20:9 + | +LL | fn env0<'any>() { + | ---- lifetime `'any` defined here +LL | _ = TYPE_OUTLIVES_0::<'static, &'any ()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/implied-outlives-bounds.rs:25:9 + | +LL | fn env1<'any>() { + | ---- lifetime `'any` defined here +LL | _ = REGION_OUTLIVES_0::<'static, 'any>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/generic-const-items/implied-outlives-bounds.rs b/tests/ui/generic-const-items/implied-outlives-bounds.rs new file mode 100644 index 0000000000000..6def5a013fd2f --- /dev/null +++ b/tests/ui/generic-const-items/implied-outlives-bounds.rs @@ -0,0 +1,28 @@ +// Check that we imply outlives-bounds on free const items. +//@ revisions: pos neg +//@[pos] check-pass +#![feature(generic_const_items)] +#![feature(freeze)] // only used in the test case `TYPE_OUTLIVES_1` +#![expect(incomplete_features)] + +const REGION_OUTLIVES_0<'a, 'b>: Option<&'a &'b ()> = None; // we imply `'a: 'b` +const REGION_OUTLIVES_1<'a, 'b>: &'a &'b () = &&(); // we imply `'a: 'b` + +const TYPE_OUTLIVES_0<'a, T>: Option<&'a T> = None; // we imply `T: 'a` + +const TYPE_OUTLIVES_1<'a, T: Def>: &'a T = &T::DEF; // we imply `T: 'a` +trait Def: std::marker::Freeze { const DEF: Self; } + +// Ensure that we actually enforce these implied bounds at usage sites: + +#[cfg(neg)] +fn env0<'any>() { + _ = TYPE_OUTLIVES_0::<'static, &'any ()>; //[neg]~ ERROR lifetime may not live long enough +} + +#[cfg(neg)] +fn env1<'any>() { + _ = REGION_OUTLIVES_0::<'static, 'any>; //[neg]~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/generic-const-items/reference-outlives-referent.rs b/tests/ui/generic-const-items/reference-outlives-referent.rs index 13e4eaac39f88..7cc8ea33b410d 100644 --- a/tests/ui/generic-const-items/reference-outlives-referent.rs +++ b/tests/ui/generic-const-items/reference-outlives-referent.rs @@ -2,8 +2,16 @@ // successfully emit a diagnostic. Regression test for issue #114714. #![feature(generic_const_items)] -#![allow(incomplete_features)] +#![expect(incomplete_features)] -const Q<'a, 'b>: &'a &'b () = &&(); //~ ERROR reference has a longer lifetime than the data it references +struct S<'a>(&'a ()); + +impl<'a> S<'a> { + const K<'b>: &'a &'b () = &&(); //~ ERROR reference has a longer lifetime than the data it references +} + +const Q<'a, 'b>: () = { + let _: &'a &'b () = &&(); //~ ERROR lifetime may not live long enough +}; fn main() {} diff --git a/tests/ui/generic-const-items/reference-outlives-referent.stderr b/tests/ui/generic-const-items/reference-outlives-referent.stderr index 34553c51654bb..fcf10ce3c770b 100644 --- a/tests/ui/generic-const-items/reference-outlives-referent.stderr +++ b/tests/ui/generic-const-items/reference-outlives-referent.stderr @@ -1,20 +1,32 @@ error[E0491]: in type `&'a &'b ()`, reference has a longer lifetime than the data it references - --> $DIR/reference-outlives-referent.rs:7:18 + --> $DIR/reference-outlives-referent.rs:10:5 | -LL | const Q<'a, 'b>: &'a &'b () = &&(); - | ^^^^^^^^^^ +LL | const K<'b>: &'a &'b () = &&(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the pointer is valid for the lifetime `'a` as defined here - --> $DIR/reference-outlives-referent.rs:7:9 + --> $DIR/reference-outlives-referent.rs:9:6 | -LL | const Q<'a, 'b>: &'a &'b () = &&(); - | ^^ +LL | impl<'a> S<'a> { + | ^^ note: but the referenced data is only valid for the lifetime `'b` as defined here - --> $DIR/reference-outlives-referent.rs:7:13 + --> $DIR/reference-outlives-referent.rs:10:13 | -LL | const Q<'a, 'b>: &'a &'b () = &&(); +LL | const K<'b>: &'a &'b () = &&(); | ^^ -error: aborting due to 1 previous error +error: lifetime may not live long enough + --> $DIR/reference-outlives-referent.rs:14:12 + | +LL | const Q<'a, 'b>: () = { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let _: &'a &'b () = &&(); + | ^^^^^^^^^^ requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0491`.