aboutsummaryrefslogtreecommitdiff
path: root/src/set_checker.rs
blob: 5762d9c269b6b043ef2affab30f06770a5f05ff6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
use crate::ast::*;
use crate::check_state::*;

use std::iter::zip;
use tracing::instrument;

impl CheckState {
    #[instrument(skip(self), level = "debug", fields(%set))]
    pub fn check_set(&self, set: &Set) -> Result<Set, CheckError> {
        match set {
            Set::BuiltIn(_) => Ok(set.clone()),
            Set::Record(fields) => {
                let fields = fields
                    .iter()
                    .map(|RecordField { name, set }| {
                        let set = self.check_set(set)?;
                        Ok(RecordField {
                            name: name.clone(),
                            set,
                        })
                    })
                    .collect::<Result<Vec<_>, _>>()?;
                Ok(Set::Record(fields))
            }
            Set::Variant(fields) => {
                let fields = fields
                    .iter()
                    .map(|VariantField { name, set }| {
                        let set = self.check_set(set)?;
                        Ok(VariantField {
                            name: name.clone(),
                            set,
                        })
                    })
                    .collect::<Result<Vec<_>, _>>()?;
                Ok(Set::Variant(fields))
            }
            Set::ClaimedSet(_) => Err(CheckError::Unimplemented("instances as sets".to_string())),
            Set::Var(v) => {
                let deref = self.lookup_set(v)?;
                Ok(deref.clone())
            }
        }
    }

    fn _check_literal_set_helper(&self, claimed: &Set, should_be: Set) -> Result<(), CheckError> {
        if !self.set_equal(claimed, &should_be) {
            Err(CheckError::WrongSetForElement(claimed.clone(), should_be))
        } else {
            Ok(())
        }
    }

    #[instrument(skip(self), level = "debug", fields(%element, %set))]
    pub fn check_element(&self, element: &Element, set: &Set) -> Result<Element, CheckError> {
        match element {
            Element::Literal(lit) => {
                // we may infer the type from the element
                match lit {
                    Literal::Int(_) => {
                        self._check_literal_set_helper(set, Set::BuiltIn(BuiltIn::Int))?;
                    }
                    Literal::Nat(_) => {
                        self._check_literal_set_helper(set, Set::BuiltIn(BuiltIn::Nat))?;
                    }
                    Literal::Str(_) => {
                        self._check_literal_set_helper(set, Set::BuiltIn(BuiltIn::Str))?;
                    }
                    Literal::Bool(_) => {
                        self._check_literal_set_helper(set, Set::BuiltIn(BuiltIn::Bool))?;
                    }
                    Literal::Float(_) => {
                        self._check_literal_set_helper(set, Set::BuiltIn(BuiltIn::Float))?;
                    }
                }
                Ok(element.clone())
            }
            Element::Var(v) => {
                let lookup = self.lookup_element(v)?;
                // we have previously done the work to discover the type of
                // this element, so what we're claiming now must match!
                if !self.set_equal(set, &lookup.set) {
                    return Err(CheckError::WrongSetForElement(
                        set.clone(),
                        lookup.set.clone(),
                    ));
                }
                Ok(lookup.element.clone())
            }
            Element::Record(assignations) => {
                let rej = |reason| CheckError::ElementDoesNotBelong {
                    element: element.clone(),
                    claimed: set.clone(),
                    reason,
                };

                // make sure we are filling a record
                let fields = if let Set::Record(fields) = set {
                    Ok(fields)
                } else {
                    Err(rej("element is a record instance".to_string()))
                }?;

                let (set_fnames, set_fsets): (Vec<String>, Vec<Set>) = fields
                    .iter()
                    .map(|RecordField { name, set }| (name.clone(), set.clone()))
                    .unzip();
                let mut set_fnames_sorted = set_fnames.clone();
                set_fnames_sorted.sort();

                let (element_fnames, element_felements): (Vec<String>, Vec<&Element>) =
                    assignations
                        .iter()
                        .map(|ElemAssign { name, element }| (name.clone(), element))
                        .unzip();

                let mut element_fnames_sorted = element_fnames.clone();
                element_fnames_sorted.sort();

                if set_fnames_sorted != element_fnames_sorted {
                    return Err(rej(format!(
                        "expected [{}] but found [{}]",
                        set_fnames.join(", "),
                        element_fnames.join(", "),
                    )));
                }

                // recurse, sets have already been completely expanded
                let sub_els = zip(element_felements, set_fsets)
                    .map(|(e_f, e_s)| self.check_element(e_f, &e_s))
                    .collect::<Result<Vec<_>, _>>()?;
                // rebuild
                let assignations = zip(element_fnames, sub_els)
                    .map(|(name, element)| ElemAssign { name, element })
                    .collect();
                // resign?
                Ok(Element::Record(assignations))
            }
            Element::Project {
                element: inner,
                field,
            } => {
                // globally unique projections mean we know what the sets going
                // in and out must be
                let SetField {
                    field_set,
                    owner_set,
                } = self.lookup_record_field(field)?;

                // enforce the correct typing of the claimed result
                if !self.set_equal(set, field_set) {
                    return Err(CheckError::WrongSetForElement(
                        set.clone(),
                        field_set.clone(),
                    ));
                }

                // enforce the correct typing of the element
                let inner = self.check_element(inner, owner_set)?;

                // Unfortunately we still have to do something nasty here to obtain the data
                let Element::Record(assignations) = inner else {
                    panic!("invariant violation: check_element returned non-record for record set");
                };
                let sub_element = assignations
                    .into_iter()
                    .find(|a| a.name == *field)
                    .expect("invariant violation: record missing field that was type-checked")
                    .element
                    .clone();

                Ok(sub_element)
            }
            Element::Inject {
                element: inner,
                field,
            } => {
                // globally unique injections mean that we know what the sets
                // going in and out must be, but compared to projections their
                // roles are here interchanged
                let SetField {
                    field_set,
                    owner_set,
                } = self.lookup_variant_field(field)?;

                // enforce the correct typing of the claimed result
                if !self.set_equal(set, owner_set) {
                    return Err(CheckError::WrongSetForElement(
                        set.clone(),
                        owner_set.clone(),
                    ));
                }

                // enforce the correct typing of the element
                let element = self.check_element(inner, field_set)?;

                Ok(Element::Inject {
                    element: Box::new(element),
                    field: field.clone(),
                })
            }
            Element::Case { .. } => Err(CheckError::Unimplemented("element case".to_string())),
        }
    }
}