formality_core/
variable.rs

1use crate::cast::Upcast;
2use crate::language::CoreKind;
3use crate::language::Language;
4use crate::visit::CoreVisit;
5
6/// A term representing a variable.
7#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub enum CoreVariable<L: Language> {
9    /// A "universal free variable" is a variable that appears
10    /// free in all terms because it is bound in the environment.
11    /// Universal means that it arose from a "forall" binder.
12    /// Universal variables are a kind of placeholder meant to represent
13    /// "some value" about which you know nothing except what you are
14    /// told to assume.
15    UniversalVar(CoreUniversalVar<L>),
16
17    /// An "existential free variable" is a variable that appears
18    /// free in all terms because it is bound in the environment.
19    /// Existential means that it arose from a "exists" binder.
20    /// Existential variables are a kind of placeholder for which
21    /// you will eventually find some specific value, so the rules typically
22    /// accumulate constraints.
23    ExistentialVar(CoreExistentialVar<L>),
24
25    /// A bound variable is one that is bound by some enclosing `Binder`
26    /// in this term (or a binder about to be constructex; see `fresh_bound_var`).
27    BoundVar(CoreBoundVar<L>),
28}
29
30impl<L: Language> CoreVariable<L> {
31    pub fn kind(&self) -> CoreKind<L> {
32        match self {
33            CoreVariable::UniversalVar(v) => v.kind,
34            CoreVariable::ExistentialVar(v) => v.kind,
35            CoreVariable::BoundVar(v) => v.kind,
36        }
37    }
38
39    /// Shift a variable in through `binders` binding levels.
40    /// Only affects bound variables.
41    pub fn shift_in(&self) -> Self {
42        if let CoreVariable::BoundVar(CoreBoundVar {
43            debruijn: Some(db),
44            var_index,
45            kind,
46        }) = self
47        {
48            CoreBoundVar {
49                debruijn: Some(db.shift_in()),
50                var_index: *var_index,
51                kind: *kind,
52            }
53            .upcast()
54        } else {
55            *self
56        }
57    }
58
59    /// Shift a variable out through `binders` binding levels.
60    /// Only affects bound variables. Returns None if the variable
61    /// is bound within those binding levels.
62    pub fn shift_out(&self) -> Option<Self> {
63        if let CoreVariable::BoundVar(CoreBoundVar {
64            debruijn: Some(db),
65            var_index,
66            kind,
67        }) = self
68        {
69            db.shift_out().map(|db1| {
70                CoreBoundVar {
71                    debruijn: Some(db1),
72                    var_index: *var_index,
73                    kind: *kind,
74                }
75                .upcast()
76            })
77        } else {
78            Some(*self)
79        }
80    }
81
82    /// A variable is *free* (i.e., not bound by any internal binder)
83    /// if it is an existential variable, a universal, or has a debruijn
84    /// index of `None`. The latter occurs when you `open` a binder (and before
85    /// you close it back up again).
86    pub fn is_free(&self) -> bool {
87        match self {
88            CoreVariable::UniversalVar(_)
89            | CoreVariable::ExistentialVar(_)
90            | CoreVariable::BoundVar(CoreBoundVar {
91                debruijn: None,
92                var_index: _,
93                kind: _,
94            }) => true,
95
96            CoreVariable::BoundVar(CoreBoundVar {
97                debruijn: Some(_),
98                var_index: _,
99                kind: _,
100            }) => false,
101        }
102    }
103
104    pub fn is_universal(&self) -> bool {
105        match self {
106            CoreVariable::UniversalVar(_) => true,
107            CoreVariable::ExistentialVar(_) => false,
108            CoreVariable::BoundVar(_) => false,
109        }
110    }
111}
112
113impl<L: Language> CoreVisit<L> for CoreVariable<L> {
114    fn free_variables(&self) -> Vec<CoreVariable<L>> {
115        if self.is_free() {
116            vec![*self]
117        } else {
118            vec![]
119        }
120    }
121
122    fn size(&self) -> usize {
123        1
124    }
125
126    fn assert_valid(&self) {}
127}
128
129#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
130pub struct CoreExistentialVar<L: Language> {
131    pub kind: CoreKind<L>,
132    pub var_index: VarIndex,
133}
134
135impl<L: Language> CoreVisit<L> for CoreExistentialVar<L> {
136    fn free_variables(&self) -> Vec<CoreVariable<L>> {
137        vec![self.upcast()]
138    }
139
140    fn size(&self) -> usize {
141        1
142    }
143
144    fn assert_valid(&self) {}
145}
146
147/// A *universal variable* is a dummy variable about which nothing is known except
148/// that which we see in the environment. When we want to prove something
149/// is true for all `T` (`∀T`), we replace `T` with a universal variable.
150#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
151pub struct CoreUniversalVar<L: Language> {
152    pub kind: CoreKind<L>,
153    pub var_index: VarIndex,
154}
155
156/// Identifies a bound variable.
157#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
158pub struct CoreBoundVar<L: Language> {
159    /// Identifies the binder that contained this variable, counting "outwards".
160    /// When you create a binder with `Binder::new`,
161    /// When you open a Binder, you get back `Bound
162    pub debruijn: Option<DebruijnIndex>,
163    pub var_index: VarIndex,
164    pub kind: CoreKind<L>,
165}
166
167#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
168pub struct DebruijnIndex {
169    pub index: usize,
170}
171
172impl DebruijnIndex {
173    pub const INNERMOST: DebruijnIndex = DebruijnIndex { index: 0 };
174
175    /// Adjust this debruijn index through a binder level.
176    pub fn shift_in(&self) -> Self {
177        DebruijnIndex {
178            index: self.index + 1,
179        }
180    }
181
182    /// Adjust this debruijn index *outward* through a binder level, if possible.
183    pub fn shift_out(&self) -> Option<Self> {
184        if self.index > 0 {
185            Some(DebruijnIndex {
186                index: self.index - 1,
187            })
188        } else {
189            None
190        }
191    }
192}
193
194#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
195pub struct VarIndex {
196    pub index: usize,
197}
198
199impl VarIndex {
200    pub const ZERO: VarIndex = VarIndex { index: 0 };
201}
202
203impl std::ops::Add<usize> for VarIndex {
204    type Output = VarIndex;
205
206    fn add(self, rhs: usize) -> Self::Output {
207        VarIndex {
208            index: self.index + rhs,
209        }
210    }
211}
212
213mod cast_impls;
214mod debug_impls;