Skip to content

Commit caa10dc

Browse files
committed
Renumber universes when canonicalizing for Chalk
This is required to avoid creating large numbers of universes from each Chalk query, while still having enough universe information for lifetime errors.
1 parent 1e6d382 commit caa10dc

File tree

3 files changed

+165
-22
lines changed

3 files changed

+165
-22
lines changed

compiler/rustc_infer/src/infer/canonical/canonicalizer.rs

+156-17
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,29 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
4949
Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
5050
}
5151

52+
/// Like [Self::canonicalize_query], but preserves distinct universes. For
53+
/// example, canonicalizing `&'?0: Trait<'?1>`, where `'?0` is in `U1` and
54+
/// `'?1` is in `U3` would be canonicalized to have ?0` in `U1` and `'?1`
55+
/// in `U2`.
56+
pub fn canonicalize_chalk_query<V>(
57+
&self,
58+
value: V,
59+
query_state: &mut OriginalQueryValues<'tcx>,
60+
) -> Canonicalized<'tcx, V>
61+
where
62+
V: TypeFoldable<'tcx>,
63+
{
64+
self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
65+
66+
Canonicalizer::canonicalize(
67+
value,
68+
self,
69+
self.tcx,
70+
&CanonicalizeAllFreeRegionsPreservingUniverses,
71+
query_state,
72+
)
73+
}
74+
5275
/// Canonicalizes a query *response* `V`. When we canonicalize a
5376
/// query response, we only canonicalize unbound inference
5477
/// variables, and we leave other free regions alone. So,
@@ -133,19 +156,22 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
133156
/// maximally general query. But if we are canonicalizing a *query
134157
/// response*, then we don't typically replace free regions, as they
135158
/// must have been introduced from other parts of the system.
136-
trait CanonicalizeRegionMode {
159+
trait CanonicalizeMode {
137160
fn canonicalize_free_region<'tcx>(
138161
&self,
139162
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
140163
r: ty::Region<'tcx>,
141164
) -> ty::Region<'tcx>;
142165

143166
fn any(&self) -> bool;
167+
168+
// Do we preserve universe of variables.
169+
fn preserve_universes(&self) -> bool;
144170
}
145171

146172
struct CanonicalizeQueryResponse;
147173

148-
impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
174+
impl CanonicalizeMode for CanonicalizeQueryResponse {
149175
fn canonicalize_free_region<'tcx>(
150176
&self,
151177
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
@@ -198,11 +224,15 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
198224
fn any(&self) -> bool {
199225
false
200226
}
227+
228+
fn preserve_universes(&self) -> bool {
229+
true
230+
}
201231
}
202232

203233
struct CanonicalizeUserTypeAnnotation;
204234

205-
impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
235+
impl CanonicalizeMode for CanonicalizeUserTypeAnnotation {
206236
fn canonicalize_free_region<'tcx>(
207237
&self,
208238
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
@@ -221,11 +251,15 @@ impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
221251
fn any(&self) -> bool {
222252
false
223253
}
254+
255+
fn preserve_universes(&self) -> bool {
256+
false
257+
}
224258
}
225259

226260
struct CanonicalizeAllFreeRegions;
227261

228-
impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions {
262+
impl CanonicalizeMode for CanonicalizeAllFreeRegions {
229263
fn canonicalize_free_region<'tcx>(
230264
&self,
231265
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
@@ -237,11 +271,39 @@ impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions {
237271
fn any(&self) -> bool {
238272
true
239273
}
274+
275+
fn preserve_universes(&self) -> bool {
276+
false
277+
}
278+
}
279+
280+
struct CanonicalizeAllFreeRegionsPreservingUniverses;
281+
282+
impl CanonicalizeMode for CanonicalizeAllFreeRegionsPreservingUniverses {
283+
fn canonicalize_free_region<'tcx>(
284+
&self,
285+
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
286+
r: ty::Region<'tcx>,
287+
) -> ty::Region<'tcx> {
288+
let universe = canonicalizer.infcx.universe_of_region(r);
289+
canonicalizer.canonical_var_for_region(
290+
CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
291+
r,
292+
)
293+
}
294+
295+
fn any(&self) -> bool {
296+
true
297+
}
298+
299+
fn preserve_universes(&self) -> bool {
300+
true
301+
}
240302
}
241303

242304
struct CanonicalizeFreeRegionsOtherThanStatic;
243305

244-
impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
306+
impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic {
245307
fn canonicalize_free_region<'tcx>(
246308
&self,
247309
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
@@ -257,6 +319,10 @@ impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
257319
fn any(&self) -> bool {
258320
true
259321
}
322+
323+
fn preserve_universes(&self) -> bool {
324+
false
325+
}
260326
}
261327

262328
struct Canonicalizer<'cx, 'tcx> {
@@ -267,7 +333,7 @@ struct Canonicalizer<'cx, 'tcx> {
267333
// Note that indices is only used once `var_values` is big enough to be
268334
// heap-allocated.
269335
indices: FxHashMap<GenericArg<'tcx>, BoundVar>,
270-
canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode,
336+
canonicalize_mode: &'cx dyn CanonicalizeMode,
271337
needs_canonical_flags: TypeFlags,
272338

273339
binder_index: ty::DebruijnIndex,
@@ -311,15 +377,15 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
311377
vid, r
312378
);
313379
let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid));
314-
self.canonicalize_region_mode.canonicalize_free_region(self, r)
380+
self.canonicalize_mode.canonicalize_free_region(self, r)
315381
}
316382

317383
ty::ReStatic
318384
| ty::ReEarlyBound(..)
319385
| ty::ReFree(_)
320386
| ty::ReEmpty(_)
321387
| ty::RePlaceholder(..)
322-
| ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r),
388+
| ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r),
323389
}
324390
}
325391

@@ -337,8 +403,10 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
337403
// `TyVar(vid)` is unresolved, track its universe index in the canonicalized
338404
// result.
339405
Err(mut ui) => {
340-
// FIXME: perf problem described in #55921.
341-
ui = ty::UniverseIndex::ROOT;
406+
if !self.canonicalize_mode.preserve_universes() {
407+
// FIXME: perf problem described in #55921.
408+
ui = ty::UniverseIndex::ROOT;
409+
}
342410
self.canonicalize_ty_var(
343411
CanonicalVarInfo {
344412
kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
@@ -422,8 +490,10 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
422490
// `ConstVar(vid)` is unresolved, track its universe index in the
423491
// canonicalized result
424492
Err(mut ui) => {
425-
// FIXME: perf problem described in #55921.
426-
ui = ty::UniverseIndex::ROOT;
493+
if !self.canonicalize_mode.preserve_universes() {
494+
// FIXME: perf problem described in #55921.
495+
ui = ty::UniverseIndex::ROOT;
496+
}
427497
return self.canonicalize_const_var(
428498
CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty) },
429499
ct,
@@ -462,7 +532,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
462532
value: V,
463533
infcx: &InferCtxt<'_, 'tcx>,
464534
tcx: TyCtxt<'tcx>,
465-
canonicalize_region_mode: &dyn CanonicalizeRegionMode,
535+
canonicalize_region_mode: &dyn CanonicalizeMode,
466536
query_state: &mut OriginalQueryValues<'tcx>,
467537
) -> Canonicalized<'tcx, V>
468538
where
@@ -493,7 +563,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
493563
let mut canonicalizer = Canonicalizer {
494564
infcx,
495565
tcx,
496-
canonicalize_region_mode,
566+
canonicalize_mode: canonicalize_region_mode,
497567
needs_canonical_flags,
498568
variables: SmallVec::new(),
499569
query_state,
@@ -504,10 +574,11 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
504574

505575
// Once we have canonicalized `out_value`, it should not
506576
// contain anything that ties it to this inference context
507-
// anymore, so it should live in the global arena.
508-
debug_assert!(!out_value.needs_infer());
577+
// anymore.
578+
debug_assert!(!out_value.needs_infer() && !out_value.has_placeholders());
509579

510-
let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);
580+
let canonical_variables =
581+
tcx.intern_canonical_var_infos(&canonicalizer.universe_canonicalized_variables());
511582

512583
let max_universe = canonical_variables
513584
.iter()
@@ -527,6 +598,19 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
527598

528599
let var_values = &mut query_state.var_values;
529600

601+
let universe = info.universe();
602+
if universe != ty::UniverseIndex::ROOT {
603+
assert!(self.canonicalize_mode.preserve_universes());
604+
605+
// Insert universe into the universe map. To preserve the order of the
606+
// universes in the value being canonicalized, we don't update the
607+
// universe in `info` until we have finished canonicalizing.
608+
match query_state.universe_map.binary_search(&universe) {
609+
Err(idx) => query_state.universe_map.insert(idx, universe),
610+
Ok(_) => {}
611+
}
612+
}
613+
530614
// This code is hot. `variables` and `var_values` are usually small
531615
// (fewer than 8 elements ~95% of the time). They are SmallVec's to
532616
// avoid allocations in those cases. We also don't use `indices` to
@@ -569,6 +653,61 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
569653
}
570654
}
571655

656+
/// Replaces the universe indexes used in `var_values` with their index in
657+
/// `query_state.universe_map`. This minimizes the maximum universe used in
658+
/// the canonicalized value.
659+
fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarInfo<'tcx>; 8]> {
660+
if self.query_state.universe_map.len() == 1 {
661+
return self.variables;
662+
}
663+
664+
let reverse_universe_map: FxHashMap<ty::UniverseIndex, ty::UniverseIndex> = self
665+
.query_state
666+
.universe_map
667+
.iter()
668+
.enumerate()
669+
.map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx)))
670+
.collect();
671+
672+
self.variables
673+
.iter()
674+
.map(|v| CanonicalVarInfo {
675+
kind: match v.kind {
676+
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
677+
return *v;
678+
}
679+
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
680+
CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u]))
681+
}
682+
CanonicalVarKind::Region(u) => {
683+
CanonicalVarKind::Region(reverse_universe_map[&u])
684+
}
685+
CanonicalVarKind::Const(u, t) => {
686+
CanonicalVarKind::Const(reverse_universe_map[&u], t)
687+
}
688+
CanonicalVarKind::PlaceholderTy(placeholder) => {
689+
CanonicalVarKind::PlaceholderTy(ty::Placeholder {
690+
universe: reverse_universe_map[&placeholder.universe],
691+
..placeholder
692+
})
693+
}
694+
CanonicalVarKind::PlaceholderRegion(placeholder) => {
695+
CanonicalVarKind::PlaceholderRegion(ty::Placeholder {
696+
universe: reverse_universe_map[&placeholder.universe],
697+
..placeholder
698+
})
699+
}
700+
CanonicalVarKind::PlaceholderConst(placeholder) => {
701+
CanonicalVarKind::PlaceholderConst(ty::Placeholder {
702+
universe: reverse_universe_map[&placeholder.universe],
703+
..placeholder
704+
})
705+
}
706+
},
707+
})
708+
.collect()
709+
}
710+
572711
/// Shorthand helper that creates a canonical region variable for
573712
/// `r` (always in the root universe). The reason that we always
574713
/// put these variables into the root universe is because this

compiler/rustc_middle/src/infer/canonical.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ pub struct CanonicalVarValues<'tcx> {
6464
/// result.
6565
#[derive(Clone, Debug)]
6666
pub struct OriginalQueryValues<'tcx> {
67-
/// Map from the universes that appear in the query to the
68-
/// universes in the caller context. For the time being, we only
69-
/// ever put ROOT values into the query, so this map is very
67+
/// Map from the universes that appear in the query to the universes in the
68+
/// caller context. For all queries except `evaluate_goal` (used by Chalk),
69+
/// we only ever put ROOT values into the query, so this map is very
7070
/// simple.
7171
pub universe_map: SmallVec<[ty::UniverseIndex; 4]>,
7272

compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::traits::{
88
PredicateObligation, SelectionError, TraitEngine,
99
};
1010
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
11-
use rustc_middle::ty::{self, Ty};
11+
use rustc_middle::ty::{self, Ty, TypeFoldable};
1212

1313
pub struct FulfillmentContext<'tcx> {
1414
obligations: FxIndexSet<PredicateObligation<'tcx>>,
@@ -91,7 +91,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
9191
let environment = obligation.param_env.caller_bounds();
9292
let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
9393
let mut orig_values = OriginalQueryValues::default();
94-
let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
94+
if goal.references_error() {
95+
continue;
96+
}
97+
98+
let canonical_goal = infcx.canonicalize_chalk_query(goal, &mut orig_values);
9599

96100
match infcx.tcx.evaluate_goal(canonical_goal) {
97101
Ok(response) => {

0 commit comments

Comments
 (0)