Skip to content

Commit 5b2291c

Browse files
introduce gather_gat_bounds
1 parent 5569757 commit 5b2291c

File tree

1 file changed

+131
-123
lines changed

1 file changed

+131
-123
lines changed

compiler/rustc_typeck/src/check/wfcheck.rs

Lines changed: 131 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -298,145 +298,49 @@ fn check_gat_where_clauses(
298298
for item in
299299
associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn))
300300
{
301-
// The clauses we that we would require from this function
302-
let mut function_clauses = FxHashSet::default();
303-
304301
let id = hir::HirId::make_owner(item.def_id.expect_local());
305302
let param_env = tcx.param_env(item.def_id.expect_local());
306303

307-
let sig = tcx.fn_sig(item.def_id);
308304
// Get the signature using placeholders. In our example, this would
309305
// convert the late-bound 'a into a free region.
310-
let sig = tcx.liberate_late_bound_regions(item.def_id, sig);
311-
// Collect the arguments that are given to this GAT in the return type
312-
// of the function signature. In our example, the GAT in the return
313-
// type is `<Self as LendingIterator>::Item<'a>`, so 'a and Self are arguments.
314-
let (regions, types) =
315-
GATSubstCollector::visit(tcx, trait_item.def_id.to_def_id(), sig.output());
316-
317-
// If both regions and types are empty, then this GAT isn't in the
318-
// return type, and we shouldn't try to do clause analysis
319-
// (particularly, doing so would end up with an empty set of clauses,
320-
// since the current method would require none, and we take the
321-
// intersection of requirements of all methods)
322-
if types.is_empty() && regions.is_empty() {
323-
continue;
324-
}
306+
let sig = tcx.liberate_late_bound_regions(item.def_id, tcx.fn_sig(item.def_id));
325307

326308
// The types we can assume to be well-formed. In our example, this
327309
// would be &'a mut Self, from the first argument.
328310
let mut wf_tys = FxHashSet::default();
329311
wf_tys.extend(sig.inputs());
330312

331-
// For each region argument (e.g., 'a in our example), check for a
332-
// relationship to the type arguments (e.g., Self). If there is an
333-
// outlives relationship (`Self: 'a`), then we want to ensure that is
334-
// reflected in a where clause on the GAT itself.
335-
for (region, region_idx) in &regions {
336-
// Ignore `'static` lifetimes for the purpose of this lint: it's
337-
// because we know it outlives everything and so doesn't give meaninful
338-
// clues
339-
if region.is_static() {
340-
continue;
341-
}
342-
for (ty, ty_idx) in &types {
343-
// In our example, requires that Self: 'a
344-
if ty_known_to_outlive(tcx, id, param_env, &wf_tys, *ty, *region) {
345-
debug!(?ty_idx, ?region_idx);
346-
debug!("required clause: {} must outlive {}", ty, region);
347-
// Translate into the generic parameters of the GAT. In
348-
// our example, the type was Self, which will also be
349-
// Self in the GAT.
350-
let ty_param = generics.param_at(*ty_idx, tcx);
351-
let ty_param = tcx.mk_ty(ty::Param(ty::ParamTy {
352-
index: ty_param.index,
353-
name: ty_param.name,
354-
}));
355-
// Same for the region. In our example, 'a corresponds
356-
// to the 'me parameter.
357-
let region_param = generics.param_at(*region_idx, tcx);
358-
let region_param = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
359-
def_id: region_param.def_id,
360-
index: region_param.index,
361-
name: region_param.name,
362-
}));
363-
// The predicate we expect to see. (In our example,
364-
// `Self: 'me`.)
365-
let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(
366-
ty_param,
367-
region_param,
368-
));
369-
let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
370-
function_clauses.insert(clause);
371-
}
372-
}
373-
}
313+
// The clauses we that we would require from this function
314+
let function_clauses = gather_gat_bounds(
315+
tcx,
316+
param_env,
317+
id,
318+
sig.output(),
319+
&wf_tys,
320+
trait_item.def_id,
321+
generics,
322+
);
374323

375-
// For each region argument (e.g., 'a in our example), also check for a
376-
// relationship to the other region arguments. If there is an
377-
// outlives relationship, then we want to ensure that is
378-
// reflected in a where clause on the GAT itself.
379-
for (region_a, region_a_idx) in &regions {
380-
// Ignore `'static` lifetimes for the purpose of this lint: it's
381-
// because we know it outlives everything and so doesn't give meaninful
382-
// clues
383-
if region_a.is_static() {
384-
continue;
385-
}
386-
for (region_b, region_b_idx) in &regions {
387-
if region_a == region_b {
388-
continue;
389-
}
390-
if region_b.is_static() {
391-
continue;
324+
if let Some(function_clauses) = function_clauses {
325+
// Imagine we have:
326+
// ```
327+
// trait Foo {
328+
// type Bar<'me>;
329+
// fn gimme(&self) -> Self::Bar<'_>;
330+
// fn gimme_default(&self) -> Self::Bar<'static>;
331+
// }
332+
// ```
333+
// We only want to require clauses on `Bar` that we can prove from *all* functions (in this
334+
// case, `'me` can be `static` from `gimme_default`)
335+
match clauses.as_mut() {
336+
Some(clauses) => {
337+
clauses.drain_filter(|p| !function_clauses.contains(p));
392338
}
393-
394-
if region_known_to_outlive(tcx, id, param_env, &wf_tys, *region_a, *region_b) {
395-
debug!(?region_a_idx, ?region_b_idx);
396-
debug!("required clause: {} must outlive {}", region_a, region_b);
397-
// Translate into the generic parameters of the GAT.
398-
let region_a_param = generics.param_at(*region_a_idx, tcx);
399-
let region_a_param = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
400-
def_id: region_a_param.def_id,
401-
index: region_a_param.index,
402-
name: region_a_param.name,
403-
}));
404-
// Same for the region.
405-
let region_b_param = generics.param_at(*region_b_idx, tcx);
406-
let region_b_param = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
407-
def_id: region_b_param.def_id,
408-
index: region_b_param.index,
409-
name: region_b_param.name,
410-
}));
411-
// The predicate we expect to see.
412-
let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(
413-
region_a_param,
414-
region_b_param,
415-
));
416-
let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
417-
function_clauses.insert(clause);
339+
None => {
340+
clauses = Some(function_clauses);
418341
}
419342
}
420343
}
421-
422-
// Imagine we have:
423-
// ```
424-
// trait Foo {
425-
// type Bar<'me>;
426-
// fn gimme(&self) -> Self::Bar<'_>;
427-
// fn gimme_default(&self) -> Self::Bar<'static>;
428-
// }
429-
// ```
430-
// We only want to require clauses on `Bar` that we can prove from *all* functions (in this
431-
// case, `'me` can be `static` from `gimme_default`)
432-
match clauses.as_mut() {
433-
Some(clauses) => {
434-
clauses.drain_filter(|p| !function_clauses.contains(p));
435-
}
436-
None => {
437-
clauses = Some(function_clauses);
438-
}
439-
}
440344
}
441345

442346
// If there are any clauses that aren't provable, emit an error
@@ -515,6 +419,110 @@ fn check_gat_where_clauses(
515419
}
516420
}
517421

422+
fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
423+
tcx: TyCtxt<'tcx>,
424+
param_env: ty::ParamEnv<'tcx>,
425+
item_hir: hir::HirId,
426+
to_check: T,
427+
wf_tys: &FxHashSet<Ty<'tcx>>,
428+
gat_def_id: LocalDefId,
429+
gat_generics: &'tcx ty::Generics,
430+
) -> Option<FxHashSet<ty::Predicate<'tcx>>> {
431+
// The bounds we that we would require from this function
432+
let mut bounds = FxHashSet::default();
433+
434+
let (regions, types) = GATSubstCollector::visit(tcx, gat_def_id.to_def_id(), to_check);
435+
436+
// If both regions and types are empty, then this GAT isn't in the
437+
// return type, and we shouldn't try to do clause analysis
438+
// (particularly, doing so would end up with an empty set of clauses,
439+
// since the current method would require none, and we take the
440+
// intersection of requirements of all methods)
441+
if types.is_empty() && regions.is_empty() {
442+
return None;
443+
}
444+
445+
for (region_a, region_a_idx) in &regions {
446+
// Ignore `'static` lifetimes for the purpose of this lint: it's
447+
// because we know it outlives everything and so doesn't give meaninful
448+
// clues
449+
if let ty::ReStatic = **region_a {
450+
continue;
451+
}
452+
// For each region argument (e.g., 'a in our example), check for a
453+
// relationship to the type arguments (e.g., Self). If there is an
454+
// outlives relationship (`Self: 'a`), then we want to ensure that is
455+
// reflected in a where clause on the GAT itself.
456+
for (ty, ty_idx) in &types {
457+
// In our example, requires that Self: 'a
458+
if ty_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *ty, *region_a) {
459+
debug!(?ty_idx, ?region_a_idx);
460+
debug!("required clause: {} must outlive {}", ty, region_a);
461+
// Translate into the generic parameters of the GAT. In
462+
// our example, the type was Self, which will also be
463+
// Self in the GAT.
464+
let ty_param = gat_generics.param_at(*ty_idx, tcx);
465+
let ty_param = tcx
466+
.mk_ty(ty::Param(ty::ParamTy { index: ty_param.index, name: ty_param.name }));
467+
// Same for the region. In our example, 'a corresponds
468+
// to the 'me parameter.
469+
let region_param = gat_generics.param_at(*region_a_idx, tcx);
470+
let region_param =
471+
tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
472+
def_id: region_param.def_id,
473+
index: region_param.index,
474+
name: region_param.name,
475+
}));
476+
// The predicate we expect to see. (In our example,
477+
// `Self: 'me`.)
478+
let clause =
479+
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_param, region_param));
480+
let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
481+
bounds.insert(clause);
482+
}
483+
}
484+
485+
// For each region argument (e.g., 'a in our example), also check for a
486+
// relationship to the other region arguments. If there is an
487+
// outlives relationship, then we want to ensure that is
488+
// reflected in a where clause on the GAT itself.
489+
for (region_b, region_b_idx) in &regions {
490+
if ty::ReStatic == **region_b || region_a == region_b {
491+
continue;
492+
}
493+
if region_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *region_a, *region_b) {
494+
debug!(?region_a_idx, ?region_b_idx);
495+
debug!("required clause: {} must outlive {}", region_a, region_b);
496+
// Translate into the generic parameters of the GAT.
497+
let region_a_param = gat_generics.param_at(*region_a_idx, tcx);
498+
let region_a_param =
499+
tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
500+
def_id: region_a_param.def_id,
501+
index: region_a_param.index,
502+
name: region_a_param.name,
503+
}));
504+
// Same for the region.
505+
let region_b_param = gat_generics.param_at(*region_b_idx, tcx);
506+
let region_b_param =
507+
tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
508+
def_id: region_b_param.def_id,
509+
index: region_b_param.index,
510+
name: region_b_param.name,
511+
}));
512+
// The predicate we expect to see.
513+
let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(
514+
region_a_param,
515+
region_b_param,
516+
));
517+
let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
518+
bounds.insert(clause);
519+
}
520+
}
521+
}
522+
523+
Some(bounds)
524+
}
525+
518526
/// Given a known `param_env` and a set of well formed types, can we prove that
519527
/// `ty` outlives `region`.
520528
fn ty_known_to_outlive<'tcx>(

0 commit comments

Comments
 (0)