Skip to content

Commit 453d2db

Browse files
check all GATs at once
1 parent 7648393 commit 453d2db

File tree

1 file changed

+90
-99
lines changed

1 file changed

+90
-99
lines changed

compiler/rustc_typeck/src/check/wfcheck.rs

Lines changed: 90 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::check::{FnCtxt, Inherited};
33
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
44

55
use rustc_ast as ast;
6-
use rustc_data_structures::fx::FxHashSet;
6+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
77
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
88
use rustc_hir as hir;
99
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -258,145 +258,131 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
258258
.emit();
259259
}
260260
}
261-
262-
check_gat_where_clauses(tcx, trait_item, encl_trait_def_id);
263261
}
264262

265263
/// Require that the user writes where clauses on GATs for the implicit
266264
/// outlives bounds involving trait parameters in trait functions and
267265
/// lifetimes passed as GAT substs. See `self-outlives-lint` test.
268-
///
269-
/// This trait will be our running example. We are currently WF checking the `Item` item...
270-
///
271-
/// ```rust
272-
/// trait LendingIterator {
273-
/// type Item<'me>; // <-- WF checking this trait item
274-
///
275-
/// fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
276-
/// }
277266
/// ```
278-
fn check_gat_where_clauses(tcx: TyCtxt<'_>, gat_hir: &hir::TraitItem<'_>, gat_def_id: DefId) {
279-
let gat_item = tcx.associated_item(gat_def_id);
280-
let gat_def_id = gat_hir.def_id;
281-
// If the current trait item isn't a type, it isn't a GAT
282-
if !matches!(gat_item.kind, ty::AssocKind::Type) {
283-
return;
284-
}
285-
let gat_generics: &ty::Generics = tcx.generics_of(gat_def_id);
286-
// If the current associated type doesn't have any (own) params, it's not a GAT
287-
// FIXME(jackh726): we can also warn in the more general case
288-
if gat_generics.params.len() == 0 {
289-
return;
290-
}
291-
let associated_items: &ty::AssocItems<'_> = tcx.associated_items(gat_def_id);
292-
let mut clauses: Option<FxHashSet<ty::Predicate<'_>>> = None;
293-
// For every function in this trait...
294-
// In our example, this would be the `next` method
295-
for item in
296-
associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn))
297-
{
298-
let item_hir_id = hir::HirId::make_owner(item.def_id.expect_local());
299-
let param_env = tcx.param_env(item.def_id.expect_local());
267+
fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRef]) {
268+
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+
}
300281

301-
// Get the signature using placeholders. In our example, this would
302-
// convert the late-bound 'a into a free region.
303-
let sig = tcx.liberate_late_bound_regions(item.def_id, tcx.fn_sig(item.def_id));
282+
let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None;
283+
for item in associated_items {
284+
if !matches!(&item.kind, hir::AssocItemKind::Fn { .. }) {
285+
// FIXME: next commit will add items...
286+
continue;
287+
}
304288

305-
// The types we can assume to be well-formed. In our example, this
306-
// would be &'a mut Self, from the first argument.
307-
let mut wf_tys = FxHashSet::default();
308-
wf_tys.extend(sig.inputs());
289+
let item_def_id = item.id.def_id;
290+
// Skip our own GAT, since it would blow away the required bounds
291+
if item_def_id == gat_def_id {
292+
continue;
293+
}
309294

310-
// The clauses we that we would require from this function
311-
let function_clauses = gather_gat_bounds(
312-
tcx,
313-
param_env,
314-
item_hir_id,
315-
sig.output(),
316-
&wf_tys,
317-
gat_def_id,
318-
gat_generics,
319-
);
295+
let item_hir_id = item.id.hir_id();
296+
let param_env = tcx.param_env(item_def_id);
320297

321-
if let Some(function_clauses) = function_clauses {
322-
// Imagine we have:
323-
// ```
324-
// trait Foo {
325-
// type Bar<'me>;
326-
// fn gimme(&self) -> Self::Bar<'_>;
327-
// fn gimme_default(&self) -> Self::Bar<'static>;
328-
// }
329-
// ```
330-
// We only want to require clauses on `Bar` that we can prove from *all* functions (in this
331-
// case, `'me` can be `static` from `gimme_default`)
332-
match clauses.as_mut() {
333-
Some(clauses) => {
334-
clauses.drain_filter(|p| !function_clauses.contains(p));
335-
}
336-
None => {
337-
clauses = Some(function_clauses);
298+
// Get the signature using placeholders. In our example, this would
299+
// convert the late-bound 'a into a free region.
300+
let sig = tcx.liberate_late_bound_regions(
301+
item_def_id.to_def_id(),
302+
tcx.fn_sig(item_def_id.to_def_id()),
303+
);
304+
305+
// The types we can assume to be well-formed. In our example, this
306+
// would be &'a mut Self, from the first argument.
307+
let mut wf_tys = FxHashSet::default();
308+
wf_tys.extend(sig.inputs());
309+
310+
// The clauses we that we would require from this function
311+
let item_required_bounds = gather_gat_bounds(
312+
tcx,
313+
param_env,
314+
item_hir_id,
315+
sig.output(),
316+
&wf_tys,
317+
gat_def_id,
318+
gat_generics,
319+
);
320+
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);
338329
}
339330
}
340331
}
332+
333+
if let Some(required_bounds) = new_required_bounds {
334+
required_bounds_by_item.insert(gat_def_id, required_bounds);
335+
}
341336
}
342337

343-
// If there are any clauses that aren't provable, emit an error
344-
let clauses = clauses.unwrap_or_default();
345-
debug!(?clauses);
346-
if !clauses.is_empty() {
338+
for (gat_def_id, required_bounds) in required_bounds_by_item {
339+
let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id);
340+
debug!(?required_bounds);
347341
let param_env = tcx.param_env(gat_def_id);
342+
let gat_hir = gat_item_hir.hir_id();
348343

349-
let mut clauses: Vec<_> = clauses
344+
let mut unsatisfied_bounds: Vec<_> = required_bounds
350345
.into_iter()
351346
.filter(|clause| match clause.kind().skip_binder() {
352347
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => {
353-
!region_known_to_outlive(
354-
tcx,
355-
gat_hir.hir_id(),
356-
param_env,
357-
&FxHashSet::default(),
358-
a,
359-
b,
360-
)
348+
!region_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b)
361349
}
362350
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => {
363-
!ty_known_to_outlive(
364-
tcx,
365-
gat_hir.hir_id(),
366-
param_env,
367-
&FxHashSet::default(),
368-
a,
369-
b,
370-
)
351+
!ty_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b)
371352
}
372353
_ => bug!("Unexpected PredicateKind"),
373354
})
374-
.map(|clause| format!("{}", clause))
355+
.map(|clause| clause.to_string())
375356
.collect();
376357

377358
// We sort so that order is predictable
378-
clauses.sort();
359+
unsatisfied_bounds.sort();
379360

380-
if !clauses.is_empty() {
381-
let plural = if clauses.len() > 1 { "s" } else { "" };
361+
if !unsatisfied_bounds.is_empty() {
362+
let plural = if unsatisfied_bounds.len() > 1 { "s" } else { "" };
382363
let mut err = tcx.sess.struct_span_err(
383-
gat_hir.span,
384-
&format!("missing required bound{} on `{}`", plural, gat_hir.ident),
364+
gat_item_hir.span,
365+
&format!("missing required bound{} on `{}`", plural, gat_item_hir.ident),
385366
);
386367

387368
let suggestion = format!(
388369
"{} {}",
389-
if !gat_hir.generics.where_clause.predicates.is_empty() { "," } else { " where" },
390-
clauses.join(", "),
370+
if !gat_item_hir.generics.where_clause.predicates.is_empty() {
371+
","
372+
} else {
373+
" where"
374+
},
375+
unsatisfied_bounds.join(", "),
391376
);
392377
err.span_suggestion(
393-
gat_hir.generics.where_clause.tail_span_for_suggestion(),
378+
gat_item_hir.generics.where_clause.tail_span_for_suggestion(),
394379
&format!("add the required where clause{}", plural),
395380
suggestion,
396381
Applicability::MachineApplicable,
397382
);
398383

399-
let bound = if clauses.len() > 1 { "these bounds are" } else { "this bound is" };
384+
let bound =
385+
if unsatisfied_bounds.len() > 1 { "these bounds are" } else { "this bound is" };
400386
err.note(&format!(
401387
"{} currently required to ensure that impls have maximum flexibility",
402388
bound
@@ -1025,6 +1011,11 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
10251011

10261012
FxHashSet::default()
10271013
});
1014+
1015+
// Only check traits, don't check trait aliases
1016+
if let hir::ItemKind::Trait(_, _, _, _, items) = item.kind {
1017+
check_gat_where_clauses(tcx, items);
1018+
}
10281019
}
10291020

10301021
/// Checks all associated type defaults of trait `trait_def_id`.

0 commit comments

Comments
 (0)