Skip to content

Commit 38995c9

Browse files
committed
consult cache in projection
1 parent 6751d68 commit 38995c9

File tree

1 file changed

+89
-8
lines changed

1 file changed

+89
-8
lines changed

src/librustc/traits/project.rs

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,79 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
424424
projection_ty,
425425
depth);
426426

427+
// FIXME(#20304) For now, I am caching here, which is good, but it
428+
// means we don't capture the type variables that are created in
429+
// the case of ambiguity. Which means we may create a large stream
430+
// of such variables. OTOH, if we move the caching up a level, we
431+
// would not benefit from caching when proving `T: Trait<U=Foo>`
432+
// bounds. It might be the case that we want two distinct caches,
433+
// or else another kind of cache entry.
434+
435+
match infcx.projection_cache.borrow_mut().try_start(projection_ty) {
436+
Ok(()) => { }
437+
Err(ProjectionCacheEntry::Ambiguous) => {
438+
// If we found ambiguity the last time, that generally
439+
// means we will continue to do so until some type in the
440+
// key changes (and we know it hasn't, because we just
441+
// fully resolved it). One exception though is closure
442+
// types, which can transition from having a fixed kind to
443+
// no kind with no visible change in the key.
444+
//
445+
// FIXME(#32286) refactor this so that closure type
446+
// changes
447+
debug!("opt_normalize_projection_type: \
448+
found cache entry: ambiguous");
449+
if !projection_ty.has_closure_types() {
450+
return None;
451+
}
452+
}
453+
Err(ProjectionCacheEntry::InProgress) => {
454+
// If while normalized A::B, we are asked to normalize
455+
// A::B, just return A::B itself. This is a conservative
456+
// answer, in the sense that A::B *is* clearly equivalent
457+
// to A::B, though there may be a better value we can
458+
// find.
459+
460+
// Under lazy normalization, this can arise when
461+
// bootstrapping. That is, imagine an environment with a
462+
// where-clause like `A::B == u32`. Now, if we are asked
463+
// to normalize `A::B`, we will want to check the
464+
// where-clauses in scope. So we will try to unify `A::B`
465+
// with `A::B`, which can trigger a recursive
466+
// normalization. In that case, I think we will want this code:
467+
//
468+
// ```
469+
// let ty = selcx.tcx().mk_projection(projection_ty.trait_ref,
470+
// projection_ty.item_name);
471+
// return Some(NormalizedTy { value: v, obligations: vec![] });
472+
// ```
473+
474+
debug!("opt_normalize_projection_type: \
475+
found cache entry: in-progress");
476+
477+
// But for now, let's classify this as an overflow:
478+
let recursion_limit = selcx.tcx().sess.recursion_limit.get();
479+
let obligation = Obligation::with_depth(cause.clone(),
480+
recursion_limit,
481+
projection_ty);
482+
selcx.infcx().report_overflow_error(&obligation, false);
483+
}
484+
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
485+
// If we find the value in the cache, then the obligations
486+
// have already been returned from the previous entry (and
487+
// should therefore have been honored).
488+
debug!("opt_normalize_projection_type: \
489+
found normalized ty `{:?}`",
490+
ty);
491+
return Some(NormalizedTy { value: ty, obligations: vec![] });
492+
}
493+
Err(ProjectionCacheEntry::Error) => {
494+
debug!("opt_normalize_projection_type: \
495+
found error");
496+
return Some(normalize_to_error(selcx, projection_ty, cause, depth));
497+
}
498+
}
499+
427500
let obligation = Obligation::with_depth(cause.clone(), depth, projection_ty.clone());
428501
match project_type(selcx, &obligation) {
429502
Ok(ProjectedTy::Progress(Progress { ty: projected_ty,
@@ -454,31 +527,37 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
454527
depth);
455528

456529
obligations.extend(normalizer.obligations);
457-
Some(Normalized {
530+
Normalized {
458531
value: normalized_ty,
459532
obligations: obligations,
460-
})
533+
}
461534
} else {
462-
Some(Normalized {
535+
Normalized {
463536
value: projected_ty,
464537
obligations: obligations,
465-
})
538+
}
466539
};
467-
468-
result
540+
infcx.projection_cache.borrow_mut()
541+
.complete(projection_ty, &result, cacheable);
542+
Some(result)
469543
}
470544
Ok(ProjectedTy::NoProgress(projected_ty)) => {
471545
debug!("opt_normalize_projection_type: \
472546
projected_ty={:?} no progress",
473547
projected_ty);
474-
Some(Normalized {
548+
let result = Normalized {
475549
value: projected_ty,
476550
obligations: vec!()
477-
})
551+
};
552+
infcx.projection_cache.borrow_mut()
553+
.complete(projection_ty, &result, true);
554+
Some(result)
478555
}
479556
Err(ProjectionTyError::TooManyCandidates) => {
480557
debug!("opt_normalize_projection_type: \
481558
too many candidates");
559+
infcx.projection_cache.borrow_mut()
560+
.ambiguous(projection_ty);
482561
None
483562
}
484563
Err(ProjectionTyError::TraitSelectionError(_)) => {
@@ -488,6 +567,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
488567
// Trait`, which when processed will cause the error to be
489568
// reported later
490569

570+
infcx.projection_cache.borrow_mut()
571+
.error(projection_ty);
491572
Some(normalize_to_error(selcx, projection_ty, cause, depth))
492573
}
493574
}

0 commit comments

Comments
 (0)