Skip to content

Commit 1772aec

Browse files
committed
subst: refactor & docs
1 parent d3922b5 commit 1772aec

File tree

2 files changed

+44
-21
lines changed

2 files changed

+44
-21
lines changed

src/opt/unfolder.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ use crate::{
4242
use crate::smt::translate_exprs::TranslateExprs;
4343

4444
pub struct Unfolder<'smt, 'ctx> {
45-
limits_ref: LimitsRef,
46-
4745
/// The expressions may contain substitutions. We keep track of those.
4846
subst: Subst<'smt>,
4947

@@ -59,11 +57,15 @@ pub struct Unfolder<'smt, 'ctx> {
5957

6058
impl<'smt, 'ctx> Unfolder<'smt, 'ctx> {
6159
pub fn new(limits_ref: LimitsRef, ctx: &'smt SmtCtx<'ctx>) -> Self {
60+
// it's important that we use the native incremental mode here, because
61+
// the performance benefit from the unfolder relies on many very fast
62+
// SAT checks.
63+
let prover = Prover::new(ctx.ctx(), IncrementalMode::Native);
64+
6265
Unfolder {
63-
limits_ref: limits_ref.clone(),
6466
subst: Subst::new(ctx.tcx(), &limits_ref),
6567
translate: TranslateExprs::new(ctx),
66-
prover: Prover::new(ctx.ctx(), IncrementalMode::Native),
68+
prover,
6769
}
6870
}
6971

@@ -147,7 +149,7 @@ impl<'smt, 'ctx> VisitorMut for Unfolder<'smt, 'ctx> {
147149
type Err = LimitError;
148150

149151
fn visit_expr(&mut self, e: &mut Expr) -> Result<(), Self::Err> {
150-
self.limits_ref.check_limits()?;
152+
self.subst.limits_ref.check_limits()?;
151153

152154
let span = e.span;
153155
let ty = e.ty.clone().unwrap();

src/vc/subst.rs

+37-16
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,35 @@ use crate::{
1919
tyctx::TyCtx,
2020
};
2121

22+
/// A stack frame of substitutions.
23+
///
24+
/// This structure uses immutable data structures, so it is cheap to clone.
2225
#[derive(Default, Clone)]
23-
struct SubstLevel {
26+
struct SubstFrame {
2427
substs: im_rc::HashMap<Ident, Expr>,
2528
free_vars: im_rc::HashSet<Ident>,
2629
}
2730

31+
/// A structure to apply variable substitutions in expressions.
2832
pub struct Subst<'a> {
2933
tcx: &'a TyCtx,
30-
cur: SubstLevel,
31-
stack: Vec<SubstLevel>,
32-
limits_ref: LimitsRef,
34+
cur: SubstFrame,
35+
stack: Vec<SubstFrame>,
36+
pub limits_ref: LimitsRef,
3337
}
3438

3539
impl<'a> Subst<'a> {
40+
/// Create a new empty instance.
3641
pub fn new(tcx: &'a TyCtx, limits_ref: &LimitsRef) -> Self {
3742
Subst {
3843
tcx,
39-
cur: SubstLevel::default(),
44+
cur: SubstFrame::default(),
4045
stack: Vec::new(),
4146
limits_ref: limits_ref.clone(),
4247
}
4348
}
4449

50+
/// Push the stack and add a substitution.
4551
pub fn push_subst(&mut self, ident: Ident, mut expr: Expr) {
4652
self.stack.push(self.cur.clone());
4753
let mut free_var_collector = FreeVariableCollector::new();
@@ -50,33 +56,48 @@ impl<'a> Subst<'a> {
5056
self.cur.substs.insert(ident, expr);
5157
}
5258

59+
/// Push the stack and handle quantified variables.
60+
///
61+
/// This function removes all given variables from the substitutions. If a
62+
/// variable is contained in the free variables of the current substitution,
63+
/// then we create a "shadow" variable that is used instead of the original
64+
/// variable to avoid name clashes.
5365
pub fn push_quant(&mut self, span: Span, vars: &mut [QuantVar], tcx: &TyCtx) {
5466
self.stack.push(self.cur.clone());
5567
for var in vars {
5668
let ident = var.name();
5769
self.cur.substs.remove(&ident);
58-
// TODO: if we removed a previous substitution, we should rebuild
59-
// the set of free variables because it might contain variables that
60-
// won't be inserted anymore.
61-
//
62-
// right now, we over-approximate the set of free variables which is
63-
// sound, but might result in too many quantified variables being
64-
// renamed.
6570

71+
// TODO: we never remove a variable from the set of free variables
72+
// in the substitutions. This is sound because we might shadow too
73+
// many variables this way, but never too few.
74+
75+
// if the variable is contained in the free variables of this
76+
// substitution, then shadow it: rename the variable and replace all
77+
// occurrences of the original variable with the new one.
6678
if self.cur.free_vars.contains(&ident) {
67-
let new_ident =
68-
tcx.clone_var(ident, span.variant(SpanVariant::Subst), VarKind::Subst);
69-
*var = QuantVar::Shadow(new_ident);
79+
tracing::trace!(ident=?ident, "shadowing quantified variable");
80+
81+
let new_span = span.variant(SpanVariant::Subst);
82+
let new_ident = tcx.clone_var(ident, new_span, VarKind::Subst);
7083
let builder = ExprBuilder::new(new_ident.span);
71-
self.cur.substs.insert(ident, builder.var(new_ident, tcx));
84+
let new_expr = builder.var(new_ident, tcx);
85+
86+
// shadow the variable
87+
*var = QuantVar::Shadow(new_ident);
88+
89+
// substitute original variable with the shadow variable
90+
self.cur.substs.insert(ident, new_expr);
7291
}
7392
}
7493
}
7594

95+
/// Pop the stack.
7696
pub fn pop(&mut self) {
7797
self.cur = self.stack.pop().expect("more calls to pop than push!");
7898
}
7999

100+
/// Lookup a variable in the current frame of substitutions.
80101
pub fn lookup_var(&self, ident: Ident) -> Option<&Expr> {
81102
self.cur.substs.get(&ident)
82103
}

0 commit comments

Comments
 (0)