Skip to content

Commit 99596aa

Browse files
committed
GCI: Imply outlives-bounds on free (generic) const items
1 parent 71e0027 commit 99596aa

12 files changed

Lines changed: 223 additions & 100 deletions

compiler/rustc_hir_analysis/src/collect/type_of.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
137137
}
138138
}
139139
}
140-
ItemKind::Const(ident, _, ty, rhs) => {
141-
if ty.is_suggestable_infer_ty() {
140+
ItemKind::Const(ident, generics, ty, rhs) => {
141+
// We can't infer the type of parameterized const items without causing a query
142+
// cycle (typeck -..> inferred_outlives_of -..> type_of -> typeck), so let's not.
143+
if generics.params.is_empty() && ty.is_suggestable_infer_ty() {
142144
infer_placeholder_type(
143145
icx.lowerer(),
144146
def_id,

compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,13 @@ use tracing::debug;
88
use super::explicit::ExplicitPredicatesMap;
99
use super::utils::*;
1010

11-
/// Infer predicates for the items in the crate.
12-
///
13-
/// `global_inferred_outlives`: this is initially the empty map that
14-
/// was generated by walking the items in the crate. This will
15-
/// now be filled with inferred predicates.
11+
/// Infer outlives-predicates for the items in the local crate.
1612
pub(super) fn infer_predicates(
1713
tcx: TyCtxt<'_>,
1814
) -> FxIndexMap<DefId, ty::EarlyBinder<'_, RequiredPredicates<'_>>> {
1915
debug!("infer_predicates");
2016

2117
let mut explicit_map = ExplicitPredicatesMap::new();
22-
2318
let mut global_inferred_outlives = FxIndexMap::default();
2419

2520
// If new predicates were added then we need to re-calculate
@@ -58,7 +53,6 @@ pub(super) fn infer_predicates(
5853
);
5954
}
6055
}
61-
6256
DefKind::TyAlias if tcx.type_alias_is_lazy(item_did) => {
6357
insert_required_predicates_to_be_wf(
6458
tcx,
@@ -69,7 +63,16 @@ pub(super) fn infer_predicates(
6963
&mut explicit_map,
7064
);
7165
}
72-
66+
DefKind::Const => {
67+
insert_required_predicates_to_be_wf(
68+
tcx,
69+
tcx.type_of(item_did).instantiate_identity(),
70+
tcx.def_span(item_did),
71+
&global_inferred_outlives,
72+
&mut item_required_predicates,
73+
&mut explicit_map,
74+
);
75+
}
7376
_ => {}
7477
};
7578

@@ -312,7 +315,7 @@ fn check_explicit_predicates<'tcx>(
312315
}
313316
}
314317

315-
/// Check the inferred predicates declared on the type.
318+
/// Check the inferred predicates of the type.
316319
///
317320
/// ### Example
318321
///

compiler/rustc_hir_analysis/src/outlives/mod.rs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,11 @@ pub(super) fn inferred_outlives_of(
1313
item_def_id: LocalDefId,
1414
) -> &[(ty::Clause<'_>, Span)] {
1515
match tcx.def_kind(item_def_id) {
16-
DefKind::Struct | DefKind::Enum | DefKind::Union => {
17-
let crate_map = tcx.inferred_outlives_crate(());
18-
crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
19-
}
20-
DefKind::TyAlias if tcx.type_alias_is_lazy(item_def_id) => {
21-
let crate_map = tcx.inferred_outlives_crate(());
22-
crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
23-
}
16+
DefKind::Struct | DefKind::Enum | DefKind::Union | DefKind::Const => {}
17+
DefKind::TyAlias if tcx.type_alias_is_lazy(item_def_id) => {}
2418
DefKind::AnonConst if tcx.features().generic_const_exprs() => {
2519
let id = tcx.local_def_id_to_hir_id(item_def_id);
26-
if tcx.hir_opt_const_param_default_param_def_id(id).is_some() {
20+
return if tcx.hir_opt_const_param_default_param_def_id(id).is_some() {
2721
// In `generics_of` we set the generics' parent to be our parent's parent which means that
2822
// we lose out on the predicates of our actual parent if we dont return those predicates here.
2923
// (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
@@ -40,21 +34,23 @@ pub(super) fn inferred_outlives_of(
4034
tcx.inferred_outlives_of(item_def_id)
4135
} else {
4236
&[]
43-
}
37+
};
4438
}
45-
_ => &[],
39+
_ => return &[],
40+
}
41+
42+
if tcx.generics_of(item_def_id).is_empty() {
43+
// Fast path.
44+
return &[];
4645
}
46+
47+
let crate_map = tcx.inferred_outlives_crate(());
48+
crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
4749
}
4850

51+
/// Compute a map from each applicable item in the local crate to its
52+
/// inferred / implied outlives-predicates.
4953
pub(super) fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
50-
// Compute a map from each ADT (struct/enum/union) and lazy type alias to
51-
// the **explicit** outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
52-
// Typically there won't be many of these, except in older code where
53-
// they were mandatory. Nonetheless, we have to ensure that every such
54-
// predicate is satisfied, so they form a kind of base set of requirements
55-
// for the type.
56-
57-
// Compute the inferred predicates
5854
let global_inferred_outlives = implicit_infer::infer_predicates(tcx);
5955

6056
// Convert the inferred predicates into the "collected" form the

src/doc/rustc-dev-guide/src/traits/implied-bounds.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ handled... well... implicitly.
99

1010
## explicit implied bounds
1111

12-
The explicit implied bounds are computed in [`fn inferred_outlives_of`]. Only ADTs and
13-
lazy type aliases have explicit implied bounds which are computed via a fixpoint algorithm
14-
in the [`fn inferred_outlives_crate`] query.
12+
The explicit implied bounds are computed in [`fn inferred_outlives_of`]. Only ADTs,
13+
[lazy type aliases][lta] and [free (generic) const items][gci] have explicit implied bounds
14+
which are computed via a fixpoint algorithm in the [`fn inferred_outlives_crate`] query.
1515

1616
We use [`fn insert_required_predicates_to_be_wf`] on all fields of all ADTs in the crate.
1717
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
3131
[`fn check_explicit_predicates`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs#L238
3232
[`fn insert_outlives_predicate`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/utils.rs#L15
3333
[nostatic]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/utils.rs#L159-L165
34+
[lta]: https://github.com/rust-lang/rust/issues/112792
35+
[gci]: https://github.com/rust-lang/rust/issues/113521
3436

3537
## implicit implied bounds
3638

tests/ui/generic-const-items/assoc-const-missing-type.rs

Lines changed: 0 additions & 20 deletions
This file was deleted.

tests/ui/generic-const-items/assoc-const-missing-type.stderr

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/implied-outlives-bounds.rs:20:9
3+
|
4+
LL | fn env0<'any>() {
5+
| ---- lifetime `'any` defined here
6+
LL | _ = TYPE_OUTLIVES_0::<'static, &'any ()>;
7+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
8+
9+
error: lifetime may not live long enough
10+
--> $DIR/implied-outlives-bounds.rs:25:9
11+
|
12+
LL | fn env1<'any>() {
13+
| ---- lifetime `'any` defined here
14+
LL | _ = REGION_OUTLIVES_0::<'static, 'any>;
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
16+
17+
error: aborting due to 2 previous errors
18+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Check that we imply outlives-bounds on free const items.
2+
//@ revisions: pos neg
3+
//@[pos] check-pass
4+
#![feature(generic_const_items)]
5+
#![feature(freeze)] // only used in the test case `TYPE_OUTLIVES_1`
6+
#![expect(incomplete_features)]
7+
8+
const REGION_OUTLIVES_0<'a, 'b>: Option<&'a &'b ()> = None; // we imply `'a: 'b`
9+
const REGION_OUTLIVES_1<'a, 'b>: &'a &'b () = &&(); // we imply `'a: 'b`
10+
11+
const TYPE_OUTLIVES_0<'a, T>: Option<&'a T> = None; // we imply `T: 'a`
12+
13+
const TYPE_OUTLIVES_1<'a, T: Def>: &'a T = &T::DEF; // we imply `T: 'a`
14+
trait Def: std::marker::Freeze { const DEF: Self; }
15+
16+
// Ensure that we actually enforce these implied bounds at usage sites:
17+
18+
#[cfg(neg)]
19+
fn env0<'any>() {
20+
_ = TYPE_OUTLIVES_0::<'static, &'any ()>; //[neg]~ ERROR lifetime may not live long enough
21+
}
22+
23+
#[cfg(neg)]
24+
fn env1<'any>() {
25+
_ = REGION_OUTLIVES_0::<'static, 'any>; //[neg]~ ERROR lifetime may not live long enough
26+
}
27+
28+
fn main() {}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Ensure that we properly deal with missing signatures and inferred types `_` in the signature.
2+
#![feature(generic_const_items)]
3+
#![expect(incomplete_features)]
4+
5+
// For implicit & explicit inferred types in the item signature we generally try to infer the
6+
// type of the body which we can then suggest to the user.
7+
8+
// However, in the type of trait impl assoc items specifically we first try to suggest the type of
9+
// the corresponding definition in the trait. While doing so, we once didn't use to instantiate GACs
10+
// correctly & couldn't handle mismatches in parameter list lengths very well (impl v trait).
11+
// issue: <https://github.com/rust-lang/rust/issues/124833>
12+
13+
trait Trait {
14+
const K<T>: T;
15+
const Q<'a>: &'a str;
16+
}
17+
18+
impl Trait for () {
19+
const K<T> = ();
20+
//~^ ERROR missing type for `const` item
21+
//~| ERROR mismatched types
22+
//~| SUGGESTION ()
23+
const Q = "";
24+
//~^ ERROR missing type for `const` item
25+
//~| ERROR lifetime parameters or bounds on associated constant `Q` do not match the trait declaration
26+
//~| SUGGESTION : &str
27+
}
28+
29+
// For parametrized free const items however, we can't typeck the body without causing a query cycle,
30+
// so we don't and thus fall back to a generic suggestion that has a placeholder.
31+
32+
const _<T> = loop {};
33+
//~^ ERROR missing type for `const` item
34+
//~| SUGGESTION <type>
35+
36+
const _<T>: _ = 0;
37+
//~^ ERROR the placeholder `_` is not allowed
38+
39+
fn main() {}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/missing-or-inferred-type.rs:19:18
3+
|
4+
LL | const K<T> = ();
5+
| - ^^ expected type parameter `T`, found `()`
6+
| |
7+
| expected this type parameter
8+
|
9+
= note: expected type parameter `T`
10+
found unit type `()`
11+
12+
error: missing type for `const` item
13+
--> $DIR/missing-or-inferred-type.rs:19:15
14+
|
15+
LL | const K<T> = ();
16+
| ^ help: provide a type for the associated constant: `()`
17+
18+
error[E0195]: lifetime parameters or bounds on associated constant `Q` do not match the trait declaration
19+
--> $DIR/missing-or-inferred-type.rs:23:13
20+
|
21+
LL | const Q<'a>: &'a str;
22+
| ---- lifetimes in impl do not match this associated constant in trait
23+
...
24+
LL | const Q = "";
25+
| ^ lifetimes do not match associated constant in trait
26+
27+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants
28+
--> $DIR/missing-or-inferred-type.rs:36:13
29+
|
30+
LL | const _<T>: _ = 0;
31+
| ^ not allowed in type signatures
32+
33+
error: missing type for `const` item
34+
--> $DIR/missing-or-inferred-type.rs:23:13
35+
|
36+
LL | const Q = "";
37+
| ^ help: provide a type for the associated constant: `: &str`
38+
39+
error: missing type for `const` item
40+
--> $DIR/missing-or-inferred-type.rs:32:11
41+
|
42+
LL | const _<T> = loop {};
43+
| ^
44+
|
45+
help: provide a type for the item
46+
|
47+
LL | const _<T>: <type> = loop {};
48+
| ++++++++
49+
50+
error: aborting due to 6 previous errors
51+
52+
Some errors have detailed explanations: E0121, E0195, E0308.
53+
For more information about an error, try `rustc --explain E0121`.

0 commit comments

Comments
 (0)