Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 4774597

Browse files
make the gat wfcheck algorithm a loop
1 parent 852a851 commit 4774597

File tree

1 file changed

+103
-57
lines changed

1 file changed

+103
-57
lines changed

compiler/rustc_typeck/src/check/wfcheck.rs

Lines changed: 103 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -266,72 +266,96 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
266266
/// ```
267267
fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRef]) {
268268
let mut required_bounds_by_item = FxHashMap::default();
269-
270-
for gat_item in associated_items {
271-
let gat_def_id = gat_item.id.def_id;
272-
let gat_item = tcx.associated_item(gat_def_id);
273-
// If this item is not an assoc ty, or has no substs, then it's not a GAT
274-
if gat_item.kind != ty::AssocKind::Type {
275-
continue;
276-
}
277-
let gat_generics = tcx.generics_of(gat_def_id);
278-
if gat_generics.params.is_empty() {
279-
continue;
280-
}
281-
282-
let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None;
283-
for item in associated_items {
284-
let item_def_id = item.id.def_id;
285-
// Skip our own GAT, since it would blow away the required bounds
286-
if item_def_id == gat_def_id {
269+
loop {
270+
let mut should_continue = false;
271+
for gat_item in associated_items {
272+
let gat_def_id = gat_item.id.def_id;
273+
let gat_item = tcx.associated_item(gat_def_id);
274+
// If this item is not an assoc ty, or has no substs, then it's not a GAT
275+
if gat_item.kind != ty::AssocKind::Type {
276+
continue;
277+
}
278+
let gat_generics = tcx.generics_of(gat_def_id);
279+
if gat_generics.params.is_empty() {
287280
continue;
288281
}
289282

290-
let item_hir_id = item.id.hir_id();
291-
let param_env = tcx.param_env(item_def_id);
283+
let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None;
284+
for item in associated_items {
285+
let item_def_id = item.id.def_id;
286+
// Skip our own GAT, since it would blow away the required bounds
287+
if item_def_id == gat_def_id {
288+
continue;
289+
}
292290

293-
let item_required_bounds = match item.kind {
294-
hir::AssocItemKind::Fn { .. } => {
295-
let sig: ty::FnSig<'_> = tcx.liberate_late_bound_regions(
296-
item_def_id.to_def_id(),
297-
tcx.fn_sig(item_def_id),
298-
);
299-
gather_gat_bounds(
300-
tcx,
301-
param_env,
302-
item_hir_id,
303-
sig.output(),
304-
&sig.inputs().iter().copied().collect(),
305-
gat_def_id,
306-
gat_generics,
307-
)
291+
let item_hir_id = item.id.hir_id();
292+
let param_env = tcx.param_env(item_def_id);
293+
294+
let item_required_bounds = match item.kind {
295+
hir::AssocItemKind::Fn { .. } => {
296+
let sig: ty::FnSig<'_> = tcx.liberate_late_bound_regions(
297+
item_def_id.to_def_id(),
298+
tcx.fn_sig(item_def_id),
299+
);
300+
gather_gat_bounds(
301+
tcx,
302+
param_env,
303+
item_hir_id,
304+
sig.output(),
305+
&sig.inputs().iter().copied().collect(),
306+
gat_def_id,
307+
gat_generics,
308+
)
309+
}
310+
hir::AssocItemKind::Type => {
311+
// If our associated item is a GAT with missing bounds, add them to
312+
// the param-env here. This allows this GAT to propagate missing bounds
313+
// to other GATs.
314+
let param_env = augment_param_env(
315+
tcx,
316+
param_env,
317+
required_bounds_by_item.get(&item_def_id),
318+
);
319+
gather_gat_bounds(
320+
tcx,
321+
param_env,
322+
item_hir_id,
323+
tcx.explicit_item_bounds(item_def_id)
324+
.iter()
325+
.copied()
326+
.collect::<Vec<_>>(),
327+
&FxHashSet::default(),
328+
gat_def_id,
329+
gat_generics,
330+
)
331+
}
332+
hir::AssocItemKind::Const => None,
333+
};
334+
335+
if let Some(item_required_bounds) = item_required_bounds {
336+
// Take the intersection of the new_required_bounds and the item_required_bounds
337+
// for this item. This is why we use an Option<_>, since we need to distinguish
338+
// the empty set of bounds from the uninitialized set of bounds.
339+
if let Some(new_required_bounds) = &mut new_required_bounds {
340+
new_required_bounds.retain(|b| item_required_bounds.contains(b));
341+
} else {
342+
new_required_bounds = Some(item_required_bounds);
343+
}
308344
}
309-
hir::AssocItemKind::Type => gather_gat_bounds(
310-
tcx,
311-
param_env,
312-
item_hir_id,
313-
tcx.explicit_item_bounds(item_def_id).iter().copied().collect::<Vec<_>>(),
314-
&FxHashSet::default(),
315-
gat_def_id,
316-
gat_generics,
317-
),
318-
hir::AssocItemKind::Const => None,
319-
};
345+
}
320346

321-
if let Some(item_required_bounds) = item_required_bounds {
322-
// Take the intersection of the new_required_bounds and the item_required_bounds
323-
// for this item. This is why we use an Option<_>, since we need to distinguish
324-
// the empty set of bounds from the uninitialized set of bounds.
325-
if let Some(new_required_bounds) = &mut new_required_bounds {
326-
new_required_bounds.retain(|b| item_required_bounds.contains(b));
327-
} else {
328-
new_required_bounds = Some(item_required_bounds);
347+
if let Some(new_required_bounds) = new_required_bounds {
348+
let required_bounds = required_bounds_by_item.entry(gat_def_id).or_default();
349+
if new_required_bounds != *required_bounds {
350+
*required_bounds = new_required_bounds;
351+
// Iterate until our required_bounds no longer change
352+
// Since they changed here, we should continue the loop
353+
should_continue = true;
329354
}
330355
}
331356
}
332-
333-
if let Some(required_bounds) = new_required_bounds {
334-
required_bounds_by_item.insert(gat_def_id, required_bounds);
357+
if !should_continue {
358+
break;
335359
}
336360
}
337361

@@ -398,6 +422,28 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
398422
}
399423
}
400424

425+
/// Add a new set of predicates to the caller_bounds of an existing param_env,
426+
/// and normalize the param_env afterwards
427+
fn augment_param_env<'tcx>(
428+
tcx: TyCtxt<'tcx>,
429+
param_env: ty::ParamEnv<'tcx>,
430+
new_predicates: Option<&FxHashSet<ty::Predicate<'tcx>>>,
431+
) -> ty::ParamEnv<'tcx> {
432+
let Some(new_predicates) = new_predicates else {
433+
return param_env;
434+
};
435+
436+
if new_predicates.is_empty() {
437+
return param_env;
438+
}
439+
440+
let bounds =
441+
tcx.mk_predicates(param_env.caller_bounds().iter().chain(new_predicates.iter().cloned()));
442+
// FIXME(compiler-errors): Perhaps there is a case where we need to normalize this
443+
// i.e. traits::normalize_param_env_or_error
444+
ty::ParamEnv::new(bounds, param_env.reveal(), param_env.constness())
445+
}
446+
401447
fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
402448
tcx: TyCtxt<'tcx>,
403449
param_env: ty::ParamEnv<'tcx>,

0 commit comments

Comments
 (0)