Get rid of constrain and solve steps

This commit is contained in:
Lukas Wirth 2024-12-28 19:19:06 +01:00
parent 85119b528a
commit 9419e199d8
2 changed files with 44 additions and 95 deletions

View file

@ -132,14 +132,6 @@ impl Generics {
self.params.len() self.params.len()
} }
pub(crate) fn len_self_lifetimes(&self) -> usize {
self.params.len_lifetimes()
}
pub(crate) fn has_trait_self(&self) -> bool {
self.params.trait_self_param().is_some()
}
/// (parent total, self param, type params, const params, impl trait list, lifetimes) /// (parent total, self param, type params, const params, impl trait list, lifetimes)
pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) { pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) {
let mut self_param = false; let mut self_param = false;

View file

@ -39,19 +39,9 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option<Ar
if count == 0 { if count == 0 {
return None; return None;
} }
let mut ctxt = Context { let variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve();
def,
has_trait_self: generics.parent_generics().map_or(false, |it| it.has_trait_self()),
len_self: generics.len_self(),
len_self_lifetimes: generics.len_self_lifetimes(),
generics,
constraints: Vec::new(),
db,
};
ctxt.build_constraints_for_item(); variances.is_empty().not().then(|| Arc::from_iter(variances))
let res = ctxt.solve();
res.is_empty().not().then(|| Arc::from_iter(res))
} }
pub(crate) fn variances_of_cycle( pub(crate) fn variances_of_cycle(
@ -172,25 +162,14 @@ struct InferredIndex(usize);
struct Context<'db> { struct Context<'db> {
db: &'db dyn HirDatabase, db: &'db dyn HirDatabase,
def: GenericDefId,
has_trait_self: bool,
len_self: usize,
len_self_lifetimes: usize,
generics: Generics, generics: Generics,
constraints: Vec<Constraint>, variances: Vec<Variance>,
}
/// Declares that the variable `decl_id` appears in a location with
/// variance `variance`.
#[derive(Clone)]
struct Constraint {
inferred: InferredIndex,
variance: Variance,
} }
impl Context<'_> { impl Context<'_> {
fn build_constraints_for_item(&mut self) { fn solve(mut self) -> Vec<Variance> {
match self.def { tracing::debug!("solve(generics={:?})", self.generics);
match self.generics.def() {
GenericDefId::AdtId(adt) => { GenericDefId::AdtId(adt) => {
let db = self.db; let db = self.db;
let mut add_constraints_from_variant = |variant| { let mut add_constraints_from_variant = |variant| {
@ -225,6 +204,26 @@ impl Context<'_> {
} }
_ => {} _ => {}
} }
let mut variances = self.variances;
// Const parameters are always invariant.
// Make all const parameters invariant.
for (idx, param) in self.generics.iter_id().enumerate() {
if let GenericParamId::ConstParamId(_) = param {
variances[idx] = Variance::Invariant;
}
}
// Functions are permitted to have unused generic parameters: make those invariant.
if let GenericDefId::FunctionId(_) = self.generics.def() {
for variance in &mut variances {
if *variance == Variance::Bivariant {
*variance = Variance::Invariant;
}
}
}
variances
} }
fn contravariant(&mut self, variance: Variance) -> Variance { fn contravariant(&mut self, variance: Variance) -> Variance {
@ -353,14 +352,8 @@ impl Context<'_> {
// Chalk has no params, so use placeholders for now? // Chalk has no params, so use placeholders for now?
TyKind::Placeholder(index) => { TyKind::Placeholder(index) => {
let idx = crate::from_placeholder_idx(self.db, *index); let idx = crate::from_placeholder_idx(self.db, *index);
let index = idx.local_id.into_raw().into_u32() as usize + self.len_self_lifetimes; let inferred = InferredIndex(self.generics.type_or_const_param_idx(idx).unwrap());
let inferred = if idx.parent == self.def { self.constrain(inferred, variance);
InferredIndex(self.has_trait_self as usize + index)
} else {
InferredIndex(self.len_self + index)
};
tracing::debug!("add_constraint(index={:?}, variance={:?})", inferred, variance);
self.constraints.push(Constraint { inferred, variance });
} }
TyKind::Function(f) => { TyKind::Function(f) => {
self.add_constraints_from_sig(f, variance); self.add_constraints_from_sig(f, variance);
@ -396,7 +389,7 @@ impl Context<'_> {
if args.is_empty() { if args.is_empty() {
return; return;
} }
if def_id == self.def { if def_id == self.generics.def() {
// HACK: Workaround for the trivial cycle salsa case (see // HACK: Workaround for the trivial cycle salsa case (see
// recursive_one_bivariant_more_non_bivariant_params test) // recursive_one_bivariant_more_non_bivariant_params test)
let variance_i = variance.xform(Variance::Bivariant); let variance_i = variance.xform(Variance::Bivariant);
@ -463,18 +456,17 @@ impl Context<'_> {
/// Adds constraints appropriate for a region appearing in a /// Adds constraints appropriate for a region appearing in a
/// context with ambient variance `variance` /// context with ambient variance `variance`
fn add_constraints_from_region(&mut self, region: &Lifetime, variance: Variance) { fn add_constraints_from_region(&mut self, region: &Lifetime, variance: Variance) {
tracing::debug!(
"add_constraints_from_region(region={:?}, variance={:?})",
region,
variance
);
match region.data(Interner) { match region.data(Interner) {
// FIXME: chalk has no params? // FIXME: chalk has no params?
LifetimeData::Placeholder(index) => { LifetimeData::Placeholder(index) => {
let idx = crate::lt_from_placeholder_idx(self.db, *index); let idx = crate::lt_from_placeholder_idx(self.db, *index);
let index = idx.local_id.into_raw().into_u32() as usize; let inferred = InferredIndex(self.generics.lifetime_idx(idx).unwrap());
let inferred = if idx.parent == self.def { self.constrain(inferred, variance);
InferredIndex(index)
} else {
InferredIndex(self.has_trait_self as usize + self.len_self + index)
};
tracing::debug!("add_constraint(index={:?}, variance={:?})", inferred, variance);
self.constraints.push(Constraint { inferred, variance: variance.clone() });
} }
LifetimeData::Static => {} LifetimeData::Static => {}
@ -513,50 +505,15 @@ impl Context<'_> {
} }
} }
} }
}
impl Context<'_> { fn constrain(&mut self, inferred: InferredIndex, variance: Variance) {
fn solve(self) -> Vec<Variance> { tracing::debug!(
let mut solutions = vec![Variance::Bivariant; self.generics.len()]; "constrain(index={:?}, variance={:?}, to={:?})",
// Propagate constraints until a fixed point is reached. Note inferred,
// that the maximum number of iterations is 2C where C is the self.variances[inferred.0],
// number of constraints (each variable can change values at most variance
// twice). Since number of constraints is linear in size of the );
// input, so is the inference process. self.variances[inferred.0] = self.variances[inferred.0].glb(variance);
let mut changed = true;
while changed {
changed = false;
for constraint in &self.constraints {
let &Constraint { inferred, variance } = constraint;
let InferredIndex(inferred) = inferred;
let old_value = solutions[inferred];
let new_value = variance.glb(old_value);
if old_value != new_value {
solutions[inferred] = new_value;
changed = true;
}
}
}
// Const parameters are always invariant.
// Make all const parameters invariant.
for (idx, param) in self.generics.iter_id().enumerate() {
if let GenericParamId::ConstParamId(_) = param {
solutions[idx] = Variance::Invariant;
}
}
// Functions are permitted to have unused generic parameters: make those invariant.
if let GenericDefId::FunctionId(_) = self.def {
for variance in &mut solutions {
if *variance == Variance::Bivariant {
*variance = Variance::Invariant;
}
}
}
solutions
} }
} }