Auto merge of #150748 - nnethercote:canonicalizer-cleanups, r=lcnr
Canonicalizer cleanups Some cleanups in and around the canonicalizers, found while I was looking closely at this code. r? @lcnr
This commit is contained in:
commit
44a5b55557
28 changed files with 163 additions and 154 deletions
|
|
@ -64,7 +64,7 @@ pub(super) struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner
|
|||
canonicalize_mode: CanonicalizeMode,
|
||||
|
||||
// Mutable fields.
|
||||
variables: &'a mut Vec<I::GenericArg>,
|
||||
variables: Vec<I::GenericArg>,
|
||||
var_kinds: Vec<CanonicalVarKind<I>>,
|
||||
variable_lookup_table: HashMap<I::GenericArg, usize>,
|
||||
/// Maps each `sub_unification_table_root_var` to the index of the first
|
||||
|
|
@ -84,14 +84,13 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
pub(super) fn canonicalize_response<T: TypeFoldable<I>>(
|
||||
delegate: &'a D,
|
||||
max_input_universe: ty::UniverseIndex,
|
||||
variables: &'a mut Vec<I::GenericArg>,
|
||||
value: T,
|
||||
) -> ty::Canonical<I, T> {
|
||||
let mut canonicalizer = Canonicalizer {
|
||||
delegate,
|
||||
canonicalize_mode: CanonicalizeMode::Response { max_input_universe },
|
||||
|
||||
variables,
|
||||
variables: Vec::new(),
|
||||
variable_lookup_table: Default::default(),
|
||||
sub_root_lookup_table: Default::default(),
|
||||
var_kinds: Vec::new(),
|
||||
|
|
@ -106,16 +105,17 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
};
|
||||
debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
|
||||
debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
|
||||
let (max_universe, variables) = canonicalizer.finalize();
|
||||
Canonical { max_universe, variables, value }
|
||||
let (max_universe, _variables, var_kinds) = canonicalizer.finalize();
|
||||
Canonical { max_universe, var_kinds, value }
|
||||
}
|
||||
|
||||
fn canonicalize_param_env(
|
||||
delegate: &'a D,
|
||||
variables: &'a mut Vec<I::GenericArg>,
|
||||
param_env: I::ParamEnv,
|
||||
) -> (I::ParamEnv, HashMap<I::GenericArg, usize>, Vec<CanonicalVarKind<I>>) {
|
||||
) -> (I::ParamEnv, Vec<I::GenericArg>, Vec<CanonicalVarKind<I>>, HashMap<I::GenericArg, usize>)
|
||||
{
|
||||
if !param_env.has_type_flags(NEEDS_CANONICAL) {
|
||||
return (param_env, Default::default(), Vec::new());
|
||||
return (param_env, Vec::new(), Vec::new(), Default::default());
|
||||
}
|
||||
|
||||
// Check whether we can use the global cache for this param_env. As we only use
|
||||
|
|
@ -129,12 +129,11 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
delegate.cx().canonical_param_env_cache_get_or_insert(
|
||||
param_env,
|
||||
|| {
|
||||
let mut variables = Vec::new();
|
||||
let mut env_canonicalizer = Canonicalizer {
|
||||
delegate,
|
||||
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
|
||||
|
||||
variables: &mut variables,
|
||||
variables: Vec::new(),
|
||||
variable_lookup_table: Default::default(),
|
||||
sub_root_lookup_table: Default::default(),
|
||||
var_kinds: Vec::new(),
|
||||
|
|
@ -147,7 +146,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
param_env,
|
||||
variable_lookup_table: env_canonicalizer.variable_lookup_table,
|
||||
var_kinds: env_canonicalizer.var_kinds,
|
||||
variables,
|
||||
variables: env_canonicalizer.variables,
|
||||
}
|
||||
},
|
||||
|&CanonicalParamEnvCacheEntry {
|
||||
|
|
@ -156,9 +155,12 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
ref variable_lookup_table,
|
||||
ref var_kinds,
|
||||
}| {
|
||||
debug_assert!(variables.is_empty());
|
||||
// FIXME(nnethercote): for reasons I don't understand, this `new`+`extend`
|
||||
// combination is faster than `variables.clone()`, because it somehow avoids
|
||||
// some allocations.
|
||||
let mut variables = Vec::new();
|
||||
variables.extend(cache_variables.iter().copied());
|
||||
(param_env, variable_lookup_table.clone(), var_kinds.clone())
|
||||
(param_env, variables, var_kinds.clone(), variable_lookup_table.clone())
|
||||
},
|
||||
)
|
||||
} else {
|
||||
|
|
@ -166,7 +168,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
delegate,
|
||||
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
|
||||
|
||||
variables,
|
||||
variables: Vec::new(),
|
||||
variable_lookup_table: Default::default(),
|
||||
sub_root_lookup_table: Default::default(),
|
||||
var_kinds: Vec::new(),
|
||||
|
|
@ -175,7 +177,12 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
};
|
||||
let param_env = param_env.fold_with(&mut env_canonicalizer);
|
||||
debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
|
||||
(param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds)
|
||||
(
|
||||
param_env,
|
||||
env_canonicalizer.variables,
|
||||
env_canonicalizer.var_kinds,
|
||||
env_canonicalizer.variable_lookup_table,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,12 +196,11 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
/// variable in the future by changing the way we detect global where-bounds.
|
||||
pub(super) fn canonicalize_input<P: TypeFoldable<I>>(
|
||||
delegate: &'a D,
|
||||
variables: &'a mut Vec<I::GenericArg>,
|
||||
input: QueryInput<I, P>,
|
||||
) -> ty::Canonical<I, QueryInput<I, P>> {
|
||||
) -> (Vec<I::GenericArg>, ty::Canonical<I, QueryInput<I, P>>) {
|
||||
// First canonicalize the `param_env` while keeping `'static`
|
||||
let (param_env, variable_lookup_table, var_kinds) =
|
||||
Canonicalizer::canonicalize_param_env(delegate, variables, input.goal.param_env);
|
||||
let (param_env, variables, var_kinds, variable_lookup_table) =
|
||||
Canonicalizer::canonicalize_param_env(delegate, input.goal.param_env);
|
||||
// Then canonicalize the rest of the input without keeping `'static`
|
||||
// while *mostly* reusing the canonicalizer from above.
|
||||
let mut rest_canonicalizer = Canonicalizer {
|
||||
|
|
@ -234,8 +240,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
|
||||
debug_assert!(!value.has_infer(), "unexpected infer in {value:?}");
|
||||
debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
|
||||
let (max_universe, variables) = rest_canonicalizer.finalize();
|
||||
Canonical { max_universe, variables, value }
|
||||
let (max_universe, variables, var_kinds) = rest_canonicalizer.finalize();
|
||||
(variables, Canonical { max_universe, var_kinds, value })
|
||||
}
|
||||
|
||||
fn get_or_insert_bound_var(
|
||||
|
|
@ -243,8 +249,9 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
arg: impl Into<I::GenericArg>,
|
||||
kind: CanonicalVarKind<I>,
|
||||
) -> ty::BoundVar {
|
||||
// FIXME: 16 is made up and arbitrary. We should look at some
|
||||
// perf data here.
|
||||
// The exact value of 16 here doesn't matter that much (8 and 32 give extremely similar
|
||||
// results). So long as we have protection against the rare cases where the length reaches
|
||||
// 1000+ (e.g. `wg-grammar`).
|
||||
let arg = arg.into();
|
||||
let idx = if self.variables.len() > 16 {
|
||||
if self.variable_lookup_table.is_empty() {
|
||||
|
|
@ -276,19 +283,18 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
ty::BoundVar::from(idx)
|
||||
}
|
||||
|
||||
fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) {
|
||||
fn finalize(self) -> (ty::UniverseIndex, Vec<I::GenericArg>, I::CanonicalVarKinds) {
|
||||
let mut var_kinds = self.var_kinds;
|
||||
// See the rustc-dev-guide section about how we deal with universes
|
||||
// during canonicalization in the new solver.
|
||||
match self.canonicalize_mode {
|
||||
let max_universe = match self.canonicalize_mode {
|
||||
// All placeholders and vars are canonicalized in the root universe.
|
||||
CanonicalizeMode::Input { .. } => {
|
||||
debug_assert!(
|
||||
var_kinds.iter().all(|var| var.universe() == ty::UniverseIndex::ROOT),
|
||||
"expected all vars to be canonicalized in root universe: {var_kinds:#?}"
|
||||
);
|
||||
let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
|
||||
(ty::UniverseIndex::ROOT, var_kinds)
|
||||
ty::UniverseIndex::ROOT
|
||||
}
|
||||
// When canonicalizing a response we map a universes already entered
|
||||
// by the caller to the root universe and only return useful universe
|
||||
|
|
@ -302,15 +308,15 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
|
|||
);
|
||||
*var = var.with_updated_universe(new_uv);
|
||||
}
|
||||
let max_universe = var_kinds
|
||||
var_kinds
|
||||
.iter()
|
||||
.map(|kind| kind.universe())
|
||||
.max()
|
||||
.unwrap_or(ty::UniverseIndex::ROOT);
|
||||
let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
|
||||
(max_universe, var_kinds)
|
||||
.unwrap_or(ty::UniverseIndex::ROOT)
|
||||
}
|
||||
}
|
||||
};
|
||||
let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
|
||||
(max_universe, self.variables, var_kinds)
|
||||
}
|
||||
|
||||
fn inner_fold_ty(&mut self, t: I::Ty) -> I::Ty {
|
||||
|
|
@ -417,7 +423,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
|
|||
// We don't canonicalize `ReStatic` in the `param_env` as we use it
|
||||
// when checking whether a `ParamEnv` candidate is global.
|
||||
ty::ReStatic => match self.canonicalize_mode {
|
||||
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
|
||||
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => {
|
||||
CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
|
||||
}
|
||||
CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
|
||||
|
|
@ -545,7 +551,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
|
|||
match self.canonicalize_mode {
|
||||
CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
|
||||
| CanonicalizeMode::Response { max_input_universe: _ } => {}
|
||||
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
|
||||
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => {
|
||||
panic!("erasing 'static in env")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,10 +59,8 @@ where
|
|||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
let mut orig_values = Default::default();
|
||||
let canonical = Canonicalizer::canonicalize_input(
|
||||
let (orig_values, canonical) = Canonicalizer::canonicalize_input(
|
||||
delegate,
|
||||
&mut orig_values,
|
||||
QueryInput {
|
||||
goal,
|
||||
predefined_opaques_in_body: delegate.cx().mk_predefined_opaques_in_body(opaque_types),
|
||||
|
|
@ -82,10 +80,7 @@ where
|
|||
I: Interner,
|
||||
T: TypeFoldable<I>,
|
||||
{
|
||||
let mut orig_values = Default::default();
|
||||
let canonical =
|
||||
Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut orig_values, value);
|
||||
canonical
|
||||
Canonicalizer::canonicalize_response(delegate, max_input_universe, value)
|
||||
}
|
||||
|
||||
/// After calling a canonical query, we apply the constraints returned
|
||||
|
|
@ -157,7 +152,7 @@ where
|
|||
//
|
||||
// We therefore instantiate the existential variable in the canonical response with the
|
||||
// inference variable of the input right away, which is more performant.
|
||||
let mut opt_values = IndexVec::from_elem_n(None, response.variables.len());
|
||||
let mut opt_values = IndexVec::from_elem_n(None, response.var_kinds.len());
|
||||
for (original_value, result_value) in iter::zip(original_values, var_values.var_values.iter()) {
|
||||
match result_value.kind() {
|
||||
ty::GenericArgKind::Type(t) => {
|
||||
|
|
@ -167,7 +162,7 @@ where
|
|||
// more involved. They are also a lot rarer than region variables.
|
||||
if let ty::Bound(index_kind, b) = t.kind()
|
||||
&& !matches!(
|
||||
response.variables.get(b.var().as_usize()).unwrap(),
|
||||
response.var_kinds.get(b.var().as_usize()).unwrap(),
|
||||
CanonicalVarKind::Ty { .. }
|
||||
)
|
||||
{
|
||||
|
|
@ -189,7 +184,7 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| {
|
||||
CanonicalVarValues::instantiate(delegate.cx(), response.var_kinds, |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
|
||||
|
|
@ -308,7 +303,7 @@ where
|
|||
let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) };
|
||||
let state = inspect::State { var_values, data };
|
||||
let state = eager_resolve_vars(delegate, state);
|
||||
Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut vec![], state)
|
||||
Canonicalizer::canonicalize_response(delegate, max_input_universe, state)
|
||||
}
|
||||
|
||||
// FIXME: needs to be pub to be accessed by downstream
|
||||
|
|
@ -345,14 +340,14 @@ where
|
|||
pub fn response_no_constraints_raw<I: Interner>(
|
||||
cx: I,
|
||||
max_universe: ty::UniverseIndex,
|
||||
variables: I::CanonicalVarKinds,
|
||||
var_kinds: I::CanonicalVarKinds,
|
||||
certainty: Certainty,
|
||||
) -> CanonicalResponse<I> {
|
||||
ty::Canonical {
|
||||
max_universe,
|
||||
variables,
|
||||
var_kinds,
|
||||
value: Response {
|
||||
var_values: ty::CanonicalVarValues::make_identity(cx, variables),
|
||||
var_values: ty::CanonicalVarValues::make_identity(cx, var_kinds),
|
||||
// FIXME: maybe we should store the "no response" version in cx, like
|
||||
// we do for cx.types and stuff.
|
||||
external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()),
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ where
|
|||
|
||||
/// The variable info for the `var_values`, only used to make an ambiguous response
|
||||
/// with no constraints.
|
||||
variables: I::CanonicalVarKinds,
|
||||
var_kinds: I::CanonicalVarKinds,
|
||||
|
||||
/// What kind of goal we're currently computing, see the enum definition
|
||||
/// for more info.
|
||||
|
|
@ -325,7 +325,7 @@ where
|
|||
// which we don't do within this evaluation context.
|
||||
max_input_universe: ty::UniverseIndex::ROOT,
|
||||
initial_opaque_types_storage_num_entries: Default::default(),
|
||||
variables: Default::default(),
|
||||
var_kinds: Default::default(),
|
||||
var_values: CanonicalVarValues::dummy(),
|
||||
current_goal_kind: CurrentGoalKind::Misc,
|
||||
origin_span,
|
||||
|
|
@ -376,7 +376,7 @@ where
|
|||
let initial_opaque_types_storage_num_entries = delegate.opaque_types_storage_num_entries();
|
||||
let mut ecx = EvalCtxt {
|
||||
delegate,
|
||||
variables: canonical_input.canonical.variables,
|
||||
var_kinds: canonical_input.canonical.var_kinds,
|
||||
var_values,
|
||||
current_goal_kind: CurrentGoalKind::from_query_input(cx, input),
|
||||
max_input_universe: canonical_input.canonical.max_universe,
|
||||
|
|
@ -1323,7 +1323,7 @@ where
|
|||
response_no_constraints_raw(
|
||||
self.cx(),
|
||||
self.max_input_universe,
|
||||
self.variables,
|
||||
self.var_kinds,
|
||||
Certainty::Maybe { cause, opaque_types_jank },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ where
|
|||
let max_input_universe = outer.max_input_universe;
|
||||
let mut nested = EvalCtxt {
|
||||
delegate,
|
||||
variables: outer.variables,
|
||||
var_kinds: outer.var_kinds,
|
||||
var_values: outer.var_values,
|
||||
current_goal_kind: outer.current_goal_kind,
|
||||
max_input_universe,
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ fn response_no_constraints<I: Interner>(
|
|||
Ok(response_no_constraints_raw(
|
||||
cx,
|
||||
input.canonical.max_universe,
|
||||
input.canonical.variables,
|
||||
input.canonical.var_kinds,
|
||||
certainty,
|
||||
))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue