Skip to content

Commit a5cbd69

Browse files
Only normalize if projections capture escaping bounds
1 parent 67b3e81 commit a5cbd69

File tree

4 files changed

+60
-20
lines changed

4 files changed

+60
-20
lines changed

compiler/rustc_middle/src/ty/flags.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,13 @@ impl FlagComputation {
163163

164164
&ty::Projection(data) => {
165165
self.add_flags(TypeFlags::HAS_TY_PROJECTION);
166+
// Check if this projection type captures any late-bound variables in its substs
167+
let old_outer = std::mem::replace(&mut self.outer_exclusive_binder, ty::INNERMOST);
166168
self.add_projection_ty(data);
169+
if self.outer_exclusive_binder > ty::INNERMOST {
170+
self.add_flags(TypeFlags::HAS_LATE_IN_PROJECTION);
171+
}
172+
self.outer_exclusive_binder = self.outer_exclusive_binder.max(old_outer);
167173
}
168174

169175
&ty::Opaque(_, substs) => {

compiler/rustc_middle/src/ty/fold.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,14 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
209209
fn still_further_specializable(&self) -> bool {
210210
self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
211211
}
212+
213+
// Indicates that this value has projection types which capture late-bound variables,
214+
// which is a sign that the projection types within need further normalization. This
215+
// is because we do not replace projections with inference variables when they capture
216+
// late-bound variables.
217+
fn has_late_bound_vars_in_projection(&self) -> bool {
218+
self.has_type_flags(TypeFlags::HAS_LATE_IN_PROJECTION)
219+
}
212220
}
213221

214222
/// This trait is implemented for every folding traversal. There is a fold

compiler/rustc_trait_selection/src/traits/fulfill.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -344,34 +344,57 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
344344
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
345345
pending_obligation.stalled_on.truncate(0);
346346

347+
let infcx = self.selcx.infcx();
347348
let obligation = &mut pending_obligation.obligation;
348349

349350
debug!(?obligation, "process_obligation pre-resolve");
350-
351351
if obligation.predicate.has_infer_types_or_consts() {
352-
obligation.predicate =
353-
self.selcx.infcx().resolve_vars_if_possible(obligation.predicate);
354-
}
355-
356-
debug!(?obligation, ?obligation.cause, "process_obligation");
352+
let mut predicate_changed = false;
357353

358-
let infcx = self.selcx.infcx();
354+
if obligation.predicate.has_late_bound_vars_in_projection() {
355+
let mut obligations = Vec::new();
356+
let predicate = crate::traits::project::try_normalize_with_depth_to(
357+
self.selcx,
358+
obligation.param_env,
359+
obligation.cause.clone(),
360+
obligation.recursion_depth + 1,
361+
obligation.predicate,
362+
&mut obligations,
363+
);
364+
if predicate != obligation.predicate {
365+
if obligations.is_empty() {
366+
debug!(
367+
"progress_changed_obligations: late-bound predicate updated, fast path: {} => {}",
368+
obligation.predicate, predicate
369+
);
370+
// Optimization, since we don't need to return with Changed if we're
371+
// just updating the predicate to a new normalized form.
372+
predicate_changed = true;
373+
obligation.predicate = predicate;
374+
} else {
375+
debug!(
376+
"progress_changed_obligations: late-bound predicate updated, slow path: {} => {}, additional obligations: {:?}",
377+
obligation.predicate, predicate, obligations
378+
);
379+
obligations.push(obligation.with(predicate));
380+
return ProcessResult::Changed(mk_pending(obligations));
381+
}
382+
} else {
383+
debug!(
384+
"progress_changed_obligations: late-bound predicate not updated: {}",
385+
obligation.predicate
386+
);
387+
}
388+
}
359389

360-
if obligation.predicate.has_projections() {
361-
let mut obligations = Vec::new();
362-
let predicate = crate::traits::project::try_normalize_with_depth_to(
363-
self.selcx,
364-
obligation.param_env,
365-
obligation.cause.clone(),
366-
obligation.recursion_depth + 1,
367-
obligation.predicate,
368-
&mut obligations,
369-
);
370-
if predicate != obligation.predicate {
371-
obligations.push(obligation.with(predicate));
372-
return ProcessResult::Changed(mk_pending(obligations));
390+
if !predicate_changed {
391+
obligation.predicate =
392+
self.selcx.infcx().resolve_vars_if_possible(obligation.predicate);
373393
}
374394
}
395+
396+
debug!(?obligation, ?obligation.cause, "process_obligation");
397+
375398
let binder = obligation.predicate.kind();
376399
match binder.no_bound_vars() {
377400
None => match binder.skip_binder() {

compiler/rustc_type_ir/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ bitflags! {
107107

108108
/// Does this value have `InferConst::Fresh`?
109109
const HAS_CT_FRESH = 1 << 19;
110+
111+
// Does this value have any late-bound vars in any projection type substs?
112+
const HAS_LATE_IN_PROJECTION = 1 << 20;
110113
}
111114
}
112115

0 commit comments

Comments
 (0)