optimize CanonicalVarValues::instantiate

This commit is contained in:
lcnr 2025-08-27 09:42:09 +02:00
parent 28a0e77d13
commit f514586408
9 changed files with 63 additions and 56 deletions

View file

@ -4681,6 +4681,7 @@ dependencies = [
name = "rustc_type_ir"
version = "0.0.0"
dependencies = [
"arrayvec",
"bitflags",
"derive-where",
"ena",

View file

@ -403,15 +403,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// special handling for this "trivial case" is a good idea.
let infcx = &self.infcx;
let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) =
let (ParamEnvAnd { param_env: _, value: self_ty }, var_values) =
infcx.instantiate_canonical(span, &query_input.canonical);
debug!(?self_ty, ?query_input, "probe_op: Mode::Path");
MethodAutoderefStepsResult {
steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
self_ty: self.make_query_response_ignoring_pending_obligations(
canonical_inference_vars,
self_ty,
),
self_ty: self
.make_query_response_ignoring_pending_obligations(var_values, self_ty),
autoderefs: 0,
from_unsafe_deref: false,
unsize: false,

View file

@ -24,7 +24,7 @@
pub use instantiate::CanonicalExt;
use rustc_index::IndexVec;
pub use rustc_middle::infer::canonical::*;
use rustc_middle::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable};
use rustc_span::Span;
use crate::infer::{InferCtxt, RegionVariableOrigin};
@ -67,29 +67,12 @@ impl<'tcx> InferCtxt<'tcx> {
.chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe()))
.collect();
let canonical_inference_vars =
self.instantiate_canonical_vars(span, canonical.variables, |ui| universes[ui]);
let result = canonical.instantiate(self.tcx, &canonical_inference_vars);
(result, canonical_inference_vars)
}
/// Given the "infos" about the canonical variables from some
/// canonical, creates fresh variables with the same
/// characteristics (see `instantiate_canonical_var` for
/// details). You can then use `instantiate` to instantiate the
/// canonical variable with these inference variables.
fn instantiate_canonical_vars(
&self,
span: Span,
variables: &List<CanonicalVarKind<'tcx>>,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> CanonicalVarValues<'tcx> {
let mut var_values = Vec::with_capacity(variables.len());
for info in variables.iter() {
let value = self.instantiate_canonical_var(span, info, &var_values, &universe_map);
var_values.push(value);
}
CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) }
let var_values =
CanonicalVarValues::instantiate(self.tcx, &canonical.variables, |var_values, info| {
self.instantiate_canonical_var(span, info, &var_values, |ui| universes[ui])
});
let result = canonical.instantiate(self.tcx, &var_values);
(result, var_values)
}
/// Given the "info" about a canonical variable, creates a fresh

View file

@ -453,16 +453,17 @@ impl<'tcx> InferCtxt<'tcx> {
// Create result arguments: if we found a value for a
// given variable in the loop above, use that. Otherwise, use
// a fresh inference variable.
let mut var_values = Vec::with_capacity(query_response.variables.len());
for (index, kind) in query_response.variables.iter().enumerate() {
let value = if kind.universe() != ty::UniverseIndex::ROOT {
let tcx = self.tcx;
let variables = query_response.variables;
let var_values = CanonicalVarValues::instantiate(tcx, variables, |var_values, kind| {
if kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all, we have to deal with them for now.
self.instantiate_canonical_var(cause.span, kind, &var_values, |u| {
universe_map[u.as_usize()]
})
} else if kind.is_existential() {
match opt_values[BoundVar::new(index)] {
match opt_values[BoundVar::new(var_values.len())] {
Some(k) => k,
None => self.instantiate_canonical_var(cause.span, kind, &var_values, |u| {
universe_map[u.as_usize()]
@ -471,20 +472,17 @@ impl<'tcx> InferCtxt<'tcx> {
} else {
// For placeholders which were already part of the input, we simply map this
// universal bound variable back the placeholder of the input.
opt_values[BoundVar::new(index)]
opt_values[BoundVar::new(var_values.len())]
.expect("expected placeholder to be unified with itself during response")
};
var_values.push(value);
}
let result_args = CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) };
}
});
let mut obligations = PredicateObligations::new();
// Carry all newly resolved opaque types to the caller's scope
for &(a, b) in &query_response.value.opaque_types {
let a = instantiate_value(self.tcx, &result_args, a);
let b = instantiate_value(self.tcx, &result_args, b);
let a = instantiate_value(self.tcx, &var_values, a);
let b = instantiate_value(self.tcx, &var_values, b);
debug!(?a, ?b, "constrain opaque type");
// We use equate here instead of, for example, just registering the
// opaque type's hidden value directly, because the hidden type may have been an inference
@ -501,7 +499,7 @@ impl<'tcx> InferCtxt<'tcx> {
);
}
Ok(InferOk { value: result_args, obligations })
Ok(InferOk { value: var_values, obligations })
}
/// Given a "guess" at the values for the canonical variables in

View file

@ -365,10 +365,8 @@ where
}
}
}
let mut var_values = Vec::with_capacity(response.variables.len());
for (index, kind) in response.variables.iter().enumerate() {
let value = if kind.universe() != ty::UniverseIndex::ROOT {
CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| {
if kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all (see the FIXME at the start of this method), we have to deal with
// them for now.
@ -383,7 +381,7 @@ where
// more placeholders then they should be able to. However the inference variables have
// to "come from somewhere", so by equating them with the original values of the caller
// later on, we pull them down into their correct universe again.
if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] {
v
} else {
delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe)
@ -392,11 +390,8 @@ where
// For placeholders which were already part of the input, we simply map this
// universal bound variable back the placeholder of the input.
original_values[kind.expect_placeholder_index()]
};
var_values.push(value)
}
CanonicalVarValues { var_values: delegate.cx().mk_args(&var_values) }
}
})
}
/// Unify the `original_values` with the `var_values` returned by the canonical query..

View file

@ -184,10 +184,9 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
{
let (infcx, key, canonical_inference_vars) =
self.build_with_canonical(DUMMY_SP, canonical_key);
let (infcx, key, var_values) = self.build_with_canonical(DUMMY_SP, canonical_key);
let ocx = ObligationCtxt::new(&infcx);
let value = operation(&ocx, key)?;
ocx.make_canonicalized_query_response(canonical_inference_vars, value)
ocx.make_canonicalized_query_response(var_values, value)
}
}

View file

@ -19,7 +19,7 @@ fn evaluate_obligation<'tcx>(
) -> Result<EvaluationResult, OverflowError> {
assert!(!tcx.next_trait_solver_globally());
debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
let (ref infcx, goal, _canonical_inference_vars) =
let (ref infcx, goal, _var_values) =
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
debug!("evaluate_obligation: goal={:#?}", goal);
let ParamEnvAnd { param_env, value: predicate } = goal;

View file

@ -5,6 +5,7 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
arrayvec = { version = "0.7", default-features = false }
bitflags = "2.4.1"
derive-where = "1.2.7"
ena = "0.14.3"

View file

@ -1,6 +1,7 @@
use std::fmt;
use std::ops::Index;
use arrayvec::ArrayVec;
use derive_where::derive_where;
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
@ -306,6 +307,37 @@ impl<I: Interner> CanonicalVarValues<I> {
CanonicalVarValues { var_values: Default::default() }
}
pub fn instantiate(
cx: I,
variables: I::CanonicalVarKinds,
mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind<I>) -> I::GenericArg,
) -> CanonicalVarValues<I> {
// Instantiating `CanonicalVarValues` is really hot, but limited to less than
// 4 most of the time. Avoid creating a `Vec` here.
if variables.len() <= 4 {
let mut var_values = ArrayVec::<_, 4>::new();
for info in variables.iter() {
var_values.push(f(&var_values, info));
}
CanonicalVarValues { var_values: cx.mk_args(&var_values) }
} else {
CanonicalVarValues::instantiate_cold(cx, variables, f)
}
}
#[cold]
fn instantiate_cold(
cx: I,
variables: I::CanonicalVarKinds,
mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind<I>) -> I::GenericArg,
) -> CanonicalVarValues<I> {
let mut var_values = Vec::with_capacity(variables.len());
for info in variables.iter() {
var_values.push(f(&var_values, info));
}
CanonicalVarValues { var_values: cx.mk_args(&var_values) }
}
#[inline]
pub fn len(&self) -> usize {
self.var_values.len()