Skip to content

Commit 602150f

Browse files
committed
Auto merge of #86946 - Aaron1011:eval-proj-obligation, r=nikomatsakis
Improve caching during trait evaluation Previously, we would 'forget' that we had `'static` regions in some place during trait evaluation. This lead to us producing `EvaluatedToOkModuloRegions` when we could have produced `EvaluatedToOk`, causing us to perform unnecessary work. This PR preserves `'static` regions when we canonicalize a predicate for `evaluate_obligation`, and when we 'freshen' a predicate during trait evaluation. Thie ensures that evaluating a predicate containing `'static` regions can produce `EvaluatedToOk` (assuming that we don't end up introducing any region dependencies during evaluation). Building off of this improved caching, we use `predicate_must_hold_considering_regions` during fulfillment of projection predicates to see if we can skip performing additional work. We already do this for trait predicates, but doing this for projection predicates lead to mixed performance results without the above caching improvements.
2 parents 32c9b7b + 3291218 commit 602150f

File tree

8 files changed

+49
-28
lines changed

8 files changed

+49
-28
lines changed

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

+5-14
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
102102
)
103103
}
104104

105-
/// A hacky variant of `canonicalize_query` that does not
106-
/// canonicalize `'static`. Unfortunately, the existing leak
107-
/// check treats `'static` differently in some cases (see also
108-
/// #33684), so if we are performing an operation that may need to
109-
/// prove "leak-check" related things, we leave `'static`
110-
/// alone.
111-
///
112-
/// `'static` is also special cased when winnowing candidates when
113-
/// selecting implementation candidates, so we also have to leave `'static`
114-
/// alone for queries that do selection.
115-
//
116-
// FIXME(#48536): once the above issues are resolved, we can remove this
117-
// and just use `canonicalize_query`.
118-
pub fn canonicalize_hr_query_hack<V>(
105+
/// A variant of `canonicalize_query` that does not
106+
/// canonicalize `'static`. This is useful when
107+
/// the query implementation can perform more efficient
108+
/// handling of `'static` regions (e.g. trait evaluation).
109+
pub fn canonicalize_query_keep_static<V>(
119110
&self,
120111
value: V,
121112
query_state: &mut OriginalQueryValues<'tcx>,

compiler/rustc_infer/src/infer/freshen.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,18 @@ pub struct TypeFreshener<'a, 'tcx> {
4747
const_freshen_count: u32,
4848
ty_freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>,
4949
const_freshen_map: FxHashMap<ty::InferConst<'tcx>, &'tcx ty::Const<'tcx>>,
50+
keep_static: bool,
5051
}
5152

5253
impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
53-
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> TypeFreshener<'a, 'tcx> {
54+
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>, keep_static: bool) -> TypeFreshener<'a, 'tcx> {
5455
TypeFreshener {
5556
infcx,
5657
ty_freshen_count: 0,
5758
const_freshen_count: 0,
5859
ty_freshen_map: Default::default(),
5960
const_freshen_map: Default::default(),
61+
keep_static,
6062
}
6163
}
6264

@@ -124,8 +126,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
124126
r
125127
}
126128

127-
ty::ReStatic
128-
| ty::ReEarlyBound(..)
129+
ty::ReEarlyBound(..)
129130
| ty::ReFree(_)
130131
| ty::ReVar(_)
131132
| ty::RePlaceholder(..)
@@ -134,6 +135,13 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
134135
// replace all free regions with 'erased
135136
self.tcx().lifetimes.re_erased
136137
}
138+
ty::ReStatic => {
139+
if self.keep_static {
140+
r
141+
} else {
142+
self.tcx().lifetimes.re_erased
143+
}
144+
}
137145
}
138146
}
139147

compiler/rustc_infer/src/infer/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
646646
}
647647

648648
pub fn freshener<'b>(&'b self) -> TypeFreshener<'b, 'tcx> {
649-
freshen::TypeFreshener::new(self)
649+
freshen::TypeFreshener::new(self, false)
650+
}
651+
652+
/// Like `freshener`, but does not replace `'static` regions.
653+
pub fn freshener_keep_static<'b>(&'b self) -> TypeFreshener<'b, 'tcx> {
654+
freshen::TypeFreshener::new(self, true)
650655
}
651656

652657
pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric {

compiler/rustc_trait_selection/src/traits/fulfill.rs

+14
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
365365
let project_obligation = obligation.with(binder.rebind(data));
366366

367367
self.process_projection_obligation(
368+
obligation,
368369
project_obligation,
369370
&mut pending_obligation.stalled_on,
370371
)
@@ -419,6 +420,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
419420
let project_obligation = obligation.with(Binder::dummy(*data));
420421

421422
self.process_projection_obligation(
423+
obligation,
422424
project_obligation,
423425
&mut pending_obligation.stalled_on,
424426
)
@@ -666,10 +668,22 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
666668

667669
fn process_projection_obligation(
668670
&mut self,
671+
obligation: &PredicateObligation<'tcx>,
669672
project_obligation: PolyProjectionObligation<'tcx>,
670673
stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
671674
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
672675
let tcx = self.selcx.tcx();
676+
677+
if obligation.predicate.is_global() {
678+
// no type variables present, can use evaluation for better caching.
679+
// FIXME: consider caching errors too.
680+
if self.selcx.infcx().predicate_must_hold_considering_regions(obligation) {
681+
return ProcessResult::Changed(vec![]);
682+
} else {
683+
tracing::debug!("Does NOT hold: {:?}", obligation);
684+
}
685+
}
686+
673687
match project::poly_project_and_unify_type(self.selcx, &project_obligation) {
674688
Ok(Ok(Some(os))) => ProcessResult::Changed(mk_pending(os)),
675689
Ok(Ok(None)) => {

compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
6464
obligation: &PredicateObligation<'tcx>,
6565
) -> Result<EvaluationResult, OverflowError> {
6666
let mut _orig_values = OriginalQueryValues::default();
67-
let c_pred = self
68-
.canonicalize_query(obligation.param_env.and(obligation.predicate), &mut _orig_values);
67+
let c_pred = self.canonicalize_query_keep_static(
68+
obligation.param_env.and(obligation.predicate),
69+
&mut _orig_values,
70+
);
6971
// Run canonical query. If overflow occurs, rerun from scratch but this time
7072
// in standard trait query mode so that overflow is handled appropriately
7173
// within `SelectionContext`.

compiler/rustc_trait_selection/src/traits/query/normalize.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
180180
// so we cannot canonicalize it.
181181
let c_data = self
182182
.infcx
183-
.canonicalize_hr_query_hack(self.param_env.and(data), &mut orig_values);
183+
.canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
184184
debug!("QueryNormalizer: c_data = {:#?}", c_data);
185185
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
186186
match tcx.normalize_projection_ty(c_data) {
@@ -249,7 +249,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
249249
// so we cannot canonicalize it.
250250
let c_data = self
251251
.infcx
252-
.canonicalize_hr_query_hack(self.param_env.and(data), &mut orig_values);
252+
.canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
253253
debug!("QueryNormalizer: c_data = {:#?}", c_data);
254254
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
255255
let normalized_ty = match tcx.normalize_projection_ty(c_data) {

compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx {
7777
}
7878

7979
// FIXME(#33684) -- We need to use
80-
// `canonicalize_hr_query_hack` here because of things
80+
// `canonicalize_query_keep_static` here because of things
8181
// like the subtype query, which go awry around
8282
// `'static` otherwise.
8383
let mut canonical_var_values = OriginalQueryValues::default();
8484
let old_param_env = query_key.param_env;
85-
let canonical_self = infcx.canonicalize_hr_query_hack(query_key, &mut canonical_var_values);
85+
let canonical_self =
86+
infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values);
8687
let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
8788

8889
let InferOk { value, obligations } = infcx

compiler/rustc_trait_selection/src/traits/select/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
216216
pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> {
217217
SelectionContext {
218218
infcx,
219-
freshener: infcx.freshener(),
219+
freshener: infcx.freshener_keep_static(),
220220
intercrate: false,
221221
intercrate_ambiguity_causes: None,
222222
allow_negative_impls: false,
@@ -227,7 +227,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
227227
pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> {
228228
SelectionContext {
229229
infcx,
230-
freshener: infcx.freshener(),
230+
freshener: infcx.freshener_keep_static(),
231231
intercrate: true,
232232
intercrate_ambiguity_causes: None,
233233
allow_negative_impls: false,
@@ -242,7 +242,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
242242
debug!(?allow_negative_impls, "with_negative");
243243
SelectionContext {
244244
infcx,
245-
freshener: infcx.freshener(),
245+
freshener: infcx.freshener_keep_static(),
246246
intercrate: false,
247247
intercrate_ambiguity_causes: None,
248248
allow_negative_impls,
@@ -257,7 +257,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
257257
debug!(?query_mode, "with_query_mode");
258258
SelectionContext {
259259
infcx,
260-
freshener: infcx.freshener(),
260+
freshener: infcx.freshener_keep_static(),
261261
intercrate: false,
262262
intercrate_ambiguity_causes: None,
263263
allow_negative_impls: false,

0 commit comments

Comments
 (0)