blob: de63f33dabc79a97b3071e33760643daf118075f [file] [log] [blame]
use std::marker::PhantomData;
use crate::cast::{Cast, CastTo};
use crate::RustIrDatabase;
use chalk_ir::fold::{Fold, Shift};
use chalk_ir::interner::{HasInterner, Interner};
use chalk_ir::*;
use tracing::{debug, instrument};
/// The "clause builder" is a useful tool for building up sets of
/// program clauses. It takes ownership of the output vector while it
/// lasts, and offers methods like `push_clause` and so forth to
/// append to it.
pub struct ClauseBuilder<'me, I: Interner> {
pub db: &'me dyn RustIrDatabase<I>,
clauses: &'me mut Vec<ProgramClause<I>>,
binders: Vec<VariableKind<I>>,
parameters: Vec<GenericArg<I>>,
}
impl<'me, I: Interner> ClauseBuilder<'me, I> {
pub fn new(db: &'me dyn RustIrDatabase<I>, clauses: &'me mut Vec<ProgramClause<I>>) -> Self {
Self {
db,
clauses,
binders: vec![],
parameters: vec![],
}
}
/// Pushes a "fact" `forall<..> { consequence }` into the set of
/// program clauses, meaning something that we can assume to be
/// true unconditionally. The `forall<..>` binders will be
/// whichever binders have been pushed (see `push_binders`).
pub fn push_fact(&mut self, consequence: impl CastTo<DomainGoal<I>>) {
self.push_clause(consequence, None::<Goal<_>>);
}
/// Pushes a "fact" `forall<..> { consequence }` into the set of
/// program clauses, meaning something that we can assume to be
/// true unconditionally. The `forall<..>` binders will be
/// whichever binders have been pushed (see `push_binders`).
pub fn push_fact_with_priority(
&mut self,
consequence: impl CastTo<DomainGoal<I>>,
constraints: impl IntoIterator<Item = InEnvironment<Constraint<I>>>,
priority: ClausePriority,
) {
self.push_clause_with_priority(consequence, None::<Goal<_>>, constraints, priority);
}
/// Pushes a clause `forall<..> { consequence :- conditions }`
/// into the set of program clauses, meaning that `consequence`
/// can be proven if `conditions` are all true. The `forall<..>`
/// binders will be whichever binders have been pushed (see `push_binders`).
pub fn push_clause(
&mut self,
consequence: impl CastTo<DomainGoal<I>>,
conditions: impl IntoIterator<Item = impl CastTo<Goal<I>>>,
) {
self.push_clause_with_priority(consequence, conditions, None, ClausePriority::High)
}
pub fn push_fact_with_constraints(
&mut self,
consequence: impl CastTo<DomainGoal<I>>,
constraints: impl IntoIterator<Item = InEnvironment<Constraint<I>>>,
) {
self.push_fact_with_priority(consequence, constraints, ClausePriority::High)
}
/// Pushes a clause `forall<..> { consequence :- conditions ; constraints }`
/// into the set of program clauses, meaning that `consequence`
/// can be proven if `conditions` are all true and `constraints`
/// are proven to hold. The `forall<..>` binders will be whichever binders
/// have been pushed (see `push_binders`).
pub fn push_clause_with_priority(
&mut self,
consequence: impl CastTo<DomainGoal<I>>,
conditions: impl IntoIterator<Item = impl CastTo<Goal<I>>>,
constraints: impl IntoIterator<Item = InEnvironment<Constraint<I>>>,
priority: ClausePriority,
) {
let interner = self.db.interner();
let clause = ProgramClauseImplication {
consequence: consequence.cast(interner),
conditions: Goals::from_iter(interner, conditions),
constraints: Constraints::from_iter(interner, constraints),
priority,
};
let clause = if self.binders.is_empty() {
// Compensate for the added empty binder
clause.shifted_in(interner)
} else {
clause
};
self.clauses.push(
ProgramClauseData(Binders::new(
VariableKinds::from_iter(interner, self.binders.clone()),
clause,
))
.intern(interner),
);
debug!("pushed clause {:?}", self.clauses.last());
}
/// Accesses the placeholders for the current list of parameters in scope.
pub fn placeholders_in_scope(&self) -> &[GenericArg<I>] {
&self.parameters
}
/// Accesses the placeholders for the current list of parameters in scope,
/// in the form of a `Substitution`.
pub fn substitution_in_scope(&self) -> Substitution<I> {
Substitution::from_iter(
self.db.interner(),
self.placeholders_in_scope().iter().cloned(),
)
}
/// Executes `op` with the `binders` in-scope; `op` is invoked
/// with the bound value `v` as a parameter. After `op` finishes,
/// the binders are popped from scope.
///
/// The new binders are always pushed onto the end of the internal
/// list of binders; this means that any extant values where were
/// created referencing the *old* list of binders are still valid.
#[instrument(level = "debug", skip(self, op))]
pub fn push_binders<R, V>(
&mut self,
binders: Binders<V>,
op: impl FnOnce(&mut Self, V::Result) -> R,
) -> R
where
V: Fold<I> + HasInterner<Interner = I>,
V::Result: std::fmt::Debug,
{
let old_len = self.binders.len();
let interner = self.interner();
self.binders.extend(binders.binders.iter(interner).cloned());
self.parameters.extend(
binders
.binders
.iter(interner)
.zip(old_len..)
.map(|(pk, i)| (i, pk).to_generic_arg(interner)),
);
let value = binders.substitute(self.interner(), &self.parameters[old_len..]);
debug!(?value);
let res = op(self, value);
self.binders.truncate(old_len);
self.parameters.truncate(old_len);
res
}
/// Push a single binder, for a type, at the end of the binder
/// list. The indices of previously bound variables are
/// unaffected and hence the context remains usable. Invokes `op`,
/// passing a type representing this new type variable in as an
/// argument.
pub fn push_bound_ty(&mut self, op: impl FnOnce(&mut Self, Ty<I>)) {
let interner = self.interner();
let binders = Binders::new(
VariableKinds::from1(interner, VariableKind::Ty(TyVariableKind::General)),
PhantomData::<I>,
);
self.push_binders(binders, |this, PhantomData| {
let ty = this
.placeholders_in_scope()
.last()
.unwrap()
.assert_ty_ref(interner)
.clone();
op(this, ty)
});
}
/// Push a single binder, for a lifetime, at the end of the binder
/// list. The indices of previously bound variables are
/// unaffected and hence the context remains usable. Invokes `op`,
/// passing a lifetime representing this new lifetime variable in as an
/// argument.
pub fn push_bound_lifetime(&mut self, op: impl FnOnce(&mut Self, Lifetime<I>)) {
let interner = self.interner();
let binders = Binders::new(
VariableKinds::from1(interner, VariableKind::Lifetime),
PhantomData::<I>,
);
self.push_binders(binders, |this, PhantomData| {
let lifetime = this
.placeholders_in_scope()
.last()
.unwrap()
.assert_lifetime_ref(interner)
.clone();
op(this, lifetime)
});
}
pub fn interner(&self) -> I {
self.db.interner()
}
}