@@ -19,29 +19,35 @@ use crate::{
19
19
tyctx:: TyCtx ,
20
20
} ;
21
21
22
+ /// A stack frame of substitutions.
23
+ ///
24
+ /// This structure uses immutable data structures, so it is cheap to clone.
22
25
#[ derive( Default , Clone ) ]
23
- struct SubstLevel {
26
+ struct SubstFrame {
24
27
substs : im_rc:: HashMap < Ident , Expr > ,
25
28
free_vars : im_rc:: HashSet < Ident > ,
26
29
}
27
30
31
+ /// A structure to apply variable substitutions in expressions.
28
32
pub struct Subst < ' a > {
29
33
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 ,
33
37
}
34
38
35
39
impl < ' a > Subst < ' a > {
40
+ /// Create a new empty instance.
36
41
pub fn new ( tcx : & ' a TyCtx , limits_ref : & LimitsRef ) -> Self {
37
42
Subst {
38
43
tcx,
39
- cur : SubstLevel :: default ( ) ,
44
+ cur : SubstFrame :: default ( ) ,
40
45
stack : Vec :: new ( ) ,
41
46
limits_ref : limits_ref. clone ( ) ,
42
47
}
43
48
}
44
49
50
+ /// Push the stack and add a substitution.
45
51
pub fn push_subst ( & mut self , ident : Ident , mut expr : Expr ) {
46
52
self . stack . push ( self . cur . clone ( ) ) ;
47
53
let mut free_var_collector = FreeVariableCollector :: new ( ) ;
@@ -50,33 +56,48 @@ impl<'a> Subst<'a> {
50
56
self . cur . substs . insert ( ident, expr) ;
51
57
}
52
58
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.
53
65
pub fn push_quant ( & mut self , span : Span , vars : & mut [ QuantVar ] , tcx : & TyCtx ) {
54
66
self . stack . push ( self . cur . clone ( ) ) ;
55
67
for var in vars {
56
68
let ident = var. name ( ) ;
57
69
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.
65
70
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.
66
78
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 ) ;
70
83
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) ;
72
91
}
73
92
}
74
93
}
75
94
95
+ /// Pop the stack.
76
96
pub fn pop ( & mut self ) {
77
97
self . cur = self . stack . pop ( ) . expect ( "more calls to pop than push!" ) ;
78
98
}
79
99
100
+ /// Lookup a variable in the current frame of substitutions.
80
101
pub fn lookup_var ( & self , ident : Ident ) -> Option < & Expr > {
81
102
self . cur . substs . get ( & ident)
82
103
}
0 commit comments