MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
check.cpp
Go to the documentation of this file.
1#include "mim/check.h"
2
3#include <absl/container/fixed_array.h>
4#include <fe/assert.h>
5
6#include "mim/rewrite.h"
7#include "mim/rule.h"
8#include "mim/world.h"
9
10namespace mim {
11
12bool Def::needs_zonk() const {
13 if (has_dep(Dep::Hole)) {
14 for (auto mut : local_muts())
15 if (Hole::isa_set(mut)) return true;
16 }
17
18 return false;
19}
20
21const Def* Def::zonk() const { return needs_zonk() ? world().zonker().rewrite(this) : this; }
22
23const Def* Def::zonk_mut() const {
24 if (!is_set()) return this;
25
26 if (auto mut = isa_mut()) {
27 if (auto hole = mut->isa<Hole>()) {
28 auto [last, op] = hole->find();
29 return op ? op->zonk() : last;
30 }
31
32 for (auto def : deps())
33 if (def->needs_zonk()) return world().zonker().rewire_mut(mut);
34
35 if (auto imm = mut->immutabilize()) return imm;
36 return this;
37 }
38
39 return zonk();
40}
41
43 return DefVec(defs.size(), [defs](size_t i) { return defs[i]->zonk(); });
44}
45
46/*
47 * Hole
48 */
49
50std::pair<Hole*, const Def*> Hole::find() {
51 auto def = Def::op(0);
52 auto last = this;
53
54 while (def) {
55 if (auto h = def->isa_mut<Hole>()) {
56 def = h->op();
57 last = h;
58 } else {
59 break;
60 }
61 }
62
63 auto root = def ? def : last;
64
65 // path compression
66 for (auto h = this; h != last;) {
67 auto next = h->op()->as_mut<Hole>();
68 h->set(root);
69 h = next;
70 }
71
72 return {last, def};
73}
74
75const Def* Hole::tuplefy(nat_t n) {
76 if (is_set()) return this;
77
78 auto& w = world();
79 auto holes = absl::FixedArray<const Def*>(n);
80 if (auto [sigma, var] = type()->isa_binder<Sigma>(); sigma && n >= 1) {
81 auto rw = VarRewriter(var, this);
82 holes[0] = w.mut_hole(sigma->op(0));
83 for (size_t i = 1; i != n; ++i) {
84 rw.map(sigma->var(n, i - 1), holes[i - 1]);
85 holes[i] = w.mut_hole(rw.rewrite(sigma->op(i)));
86 }
87 } else {
88 for (size_t i = 0; i != n; ++i)
89 holes[i] = w.mut_hole(type()->proj(n, i));
90 }
91
92 auto tuple = w.tuple(holes);
93 set(tuple);
94 return tuple;
95}
96
97/*
98 * Checker
99 */
100
101#ifdef MIM_ENABLE_CHECKS
102template<Checker::Mode mode>
103bool Checker::fail() {
104 if (mode == Check && world().flags().break_on_alpha) fe::breakpoint();
105 return false;
106}
107
108const Def* Checker::fail() {
109 if (world().flags().break_on_alpha) fe::breakpoint();
110 return {};
111}
112#endif
113
115 if (defs.empty()) return nullptr;
116 auto first = defs.front();
117 for (size_t i = 1, e = defs.size(); i != e; ++i)
118 if (!alpha<Test>(first, defs[i])) return nullptr;
119 return first;
120}
121
122const Def* Checker::assignable_(const Def* type, const Def* val) {
123 auto val_ty = val->unfold_type()->zonk();
124 if (type == val_ty) return val;
125
126 auto& w = world();
127 if (auto sigma = type->isa<Sigma>()) {
128 if (!alpha_<Check>(type->arity(), val_ty->arity())) return fail();
129
130 size_t a = sigma->num_ops();
131 auto red = sigma->reduce(val);
132 auto new_ops = absl::FixedArray<const Def*>(red.size());
133 for (size_t i = 0; i != a; ++i) {
134 auto new_val = assignable_(red[i], val->proj(a, i));
135 if (new_val)
136 new_ops[i] = new_val;
137 else
138 return fail();
139 }
140 return w.tuple(new_ops);
141 } else if (auto uniq = val_ty->isa<Uniq>()) {
142 if (auto new_val = assignable(type, uniq->op())) return new_val;
143 return fail();
144 }
145
146 return alpha_<Check>(type, val_ty) ? val : fail();
147}
148
149template<Checker::Mode mode>
150bool Checker::alpha_(const Def* d1, const Def* d2) {
151 for (bool todo = true; todo;) {
152 // below we check type and arity which may in turn open up more opportunities for zonking
153 todo = false;
154 d1 = d1->zonk_mut();
155 d2 = d2->zonk_mut();
156
157 // It is only safe to check for pointer equality if there are no Vars involved.
158 // Otherwise, we have to look more thoroughly.
159 // Example: λx.x - λz.x
160 if (!d1->has_dep(Dep::Var) && !d2->has_dep(Dep::Var) && d1 == d2) return true;
161
162 auto h1 = d1->isa_mut<Hole>();
163 auto h2 = d2->isa_mut<Hole>();
164
165 if constexpr (mode == Check) {
166 if (h1) return h1->set(d2), true;
167 if (h2) return h2->set(d1), true;
168 } else if (h1 || h2) // mode == Test and h1 or h2 is an unresolved Hole
169 return fail<Test>();
170
171 if (!d1->is_set() || !d2->is_set()) return fail<mode>();
172
173 auto mut1 = d1->isa_mut();
174 auto mut2 = d2->isa_mut();
175
176 if (mut1 && mut2 && mut1 == mut2) return true;
177
178 // Globals are HACKs and require additionaly HACKs:
179 // Unless they are pointer equal (above) always consider them unequal.
180 if (d1->isa<Global>() || d2->isa<Global>()) return false;
181
182 if (auto [i, ins] = bind(mut1, d2); !ins) return i->second == d2;
183 if (auto [i, ins] = bind(mut2, d1); !ins) return i->second == d1;
184
185 if (d1->isa<Top>() || d2->isa<Top>()) return mode == Check;
186
187 if (auto t1 = d1->type()) {
188 if (auto t2 = d2->type()) {
189 if (!alpha_<mode>(t1, t2)) return fail<mode>();
190 }
191 }
192
193 if (!alpha_<mode>(d1->arity(), d2->arity())) return fail<mode>();
194
195 auto new_d1 = d1->zonk_mut();
196 auto new_d2 = d2->zonk_mut();
197 if (new_d1 != d1 || new_d2 != d2) {
198 todo = true;
199 d1 = new_d1;
200 d2 = new_d2;
201 }
202 }
203
204 auto seq1 = d1->isa<Seq>();
205 auto seq2 = d2->isa<Seq>();
206
207 if constexpr (mode == Mode::Check) {
208 if (auto umax = d1->isa<UMax>(); umax && !d2->isa<UMax>()) return check(umax, d2);
209 if (auto umax = d2->isa<UMax>(); umax && !d1->isa<UMax>()) return check(umax, d1);
210
211 if (seq1 && seq1->arity() == world().lit_nat_1() && !seq2) return check1(seq1, d2);
212 if (seq2 && seq2->arity() == world().lit_nat_1() && !seq1) return check1(seq2, d1);
213
214 if (seq1 && seq2) {
215 if (auto mut_seq = seq1->isa_mut<Seq>(); mut_seq && seq2->isa_imm()) return check(mut_seq, seq2);
216 if (auto mut_seq = seq2->isa_mut<Seq>(); mut_seq && seq1->isa_imm()) return check(mut_seq, seq1);
217 }
218 }
219
220 if (auto prod = d1->isa<Prod>()) return check<mode>(prod, d2);
221 if (auto prod = d2->isa<Prod>()) return check<mode>(prod, d1);
222 if (seq1 && seq2) return alpha_<mode>(seq1->body(), seq2->body());
223
224 if (d1->node() != d2->node() || d1->flags() != d2->flags()) return fail<mode>();
225
226 if (auto var1 = d1->isa<Var>()) {
227 auto var2 = d2->as<Var>();
228 if (auto i = binders_.find(var1->mut()); i != binders_.end()) return i->second == var2->mut();
229 if (auto i = binders_.find(var2->mut()); i != binders_.end()) return fail<mode>(); // var2 is bound
230 // both var1 and var2 are free: OK, when they are the same or in Check mode
231 return var1 == var2 || mode == Check;
232 }
233
234 for (size_t i = 0, e = d1->num_ops(); i != e; ++i)
235 if (!alpha_<mode>(d1->op(i), d2->op(i))) return fail<mode>();
236 return true;
237}
238
239template<Checker::Mode mode>
240bool Checker::check(const Prod* prod, const Def* def) {
241 size_t a = prod->num_ops();
242 for (size_t i = 0; i != a; ++i)
243 if (!alpha_<mode>(prod->op(i), def->proj(a, i))) return fail<mode>();
244 return true;
245}
246
247// alpha(«1; body», def) -> alpha(body, def);
248bool Checker::check1(const Seq* seq, const Def* def) {
249 auto body = seq->reduce(world().lit_idx_1_0()); // try to get rid of var inside of body
250 if (!alpha_<Check>(body, def)) return fail<Check>();
251 if (auto mut_seq = seq->isa_mut<Seq>()) mut_seq->set(world().lit_nat_1(), body->zonk());
252 return true;
253}
254
255// Try to get rid of mut_seq's var: it may occur in its body and vanish after reduction
256// as holes might have been filled in the meantime.
257bool Checker::check(Seq* mut_seq, const Seq* imm_seq) {
258 auto mut_body = mut_seq->reduce(world().top(world().type_idx(mut_seq->arity())));
259 if (!alpha_<Check>(mut_body, imm_seq->body())) return fail<Check>();
260
261 mut_seq->set(mut_seq->arity(), mut_body->zonk());
262 return true;
263}
264
265bool Checker::check(const UMax* umax, const Def* def) {
266 for (auto op : umax->ops())
267 if (!alpha<Check>(op, def)) return fail<Check>();
268 return true;
269}
270
271/*
272 * infer & check
273 */
274
275const Def* Arr::check(size_t, const Def* def) { return def; } // TODO
276
277const Def* Arr::check() {
278 auto t = body()->unfold_type();
280 error(type()->loc(), "declared sort '{}' of array does not match inferred one '{}'", type(), t);
281 return t;
282}
283
285 auto elems = absl::FixedArray<const Def*>(ops.size());
286 for (size_t i = 0, e = ops.size(); i != e; ++i)
287 elems[i] = ops[i]->unfold_type();
288 return world.sigma(elems);
289}
290
292 auto elems = absl::FixedArray<const Def*>(ops.size());
293 for (size_t i = 0, e = ops.size(); i != e; ++i)
294 elems[i] = ops[i]->unfold_type();
295 return w.umax<UMax::Kind>(elems);
296}
297
298const Def* Sigma::check(size_t, const Def* def) { return def; } // TODO
299
301 auto t = infer(world(), ops());
302 if (t != type()) {
303 // TODO HACK
305 return t;
306 else {
307 world().WLOG(
308 "incorrect type '{}' for '{}'. Correct one would be: '{}'. I'll keep this one nevertheless due to "
309 "bugs in clos-conv",
310 type(), this, t);
311 return type();
312 }
313 }
314 return t;
315}
316
317const Def* Pi::infer(const Def* dom, const Def* codom) {
318 auto& w = dom->world();
319 return w.umax<UMax::Kind>({dom->unfold_type(), codom->unfold_type()});
320}
321
322const Def* Pi::check(size_t, const Def* def) { return def; }
323
324const Def* Pi::check() {
325 auto t = infer(dom(), codom());
327 error(type()->loc(), "declared sort '{}' of function type does not match inferred one '{}'", type(), t);
328 return t;
329}
330
331const Def* Lam::check(size_t i, const Def* def) {
332 if (i == 0) {
333 if (auto filter = Checker::assignable(world().type_bool(), def)) return filter;
334 throw Error().error(filter()->loc(), "filter '{}' of lambda is of type '{}' but must be of type 'Bool'",
335 filter(), filter()->type());
336 } else if (i == 1) {
337 if (auto body = Checker::assignable(codom(), def)) return body;
338 throw Error()
339 .error(def->loc(), "body of function is not assignable to declared codomain")
340 .note(def->loc(), "body: '{}'", def)
341 .note(def->loc(), "type: '{}'", def->type())
342 .note(codom()->loc(), "codomain: '{}'", codom());
343 }
344 fe::unreachable();
345}
346
347const Def* Reform::check() {
348 auto t = infer(meta_type());
350 error(type()->loc(), "declared sort '{}' of rule type does not match inferred one '{}'", type(), t);
351 return t;
352}
353
354const Def* Reform::infer(const Def* meta_type) { return meta_type->unfold_type(); }
355
356const Def* Rule::check() {
357 auto t1 = lhs()->type();
358 auto t2 = rhs()->type();
360 error(type()->loc(), "type mismatch: '{}' for lhs, but '{}' for rhs", t1, t2);
361 if (!Checker::assignable(world().type_bool(), guard()))
362 error(guard()->loc(), "condition '{}' of rewrite is of type '{}' but must be of type 'Bool'", guard(),
363 guard()->type());
364
365 return type();
366}
367
368const Def* Rule::check(size_t, const Def* def) {
369 return def;
370 // TODO: do actual check + what are the parameters ?
371}
372
373#ifndef DOXYGEN
374template bool Checker::alpha_<Checker::Check>(const Def*, const Def*);
375template bool Checker::alpha_<Checker::Test>(const Def*, const Def*);
376#endif
377
378} // namespace mim
const Def * check() final
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Definition check.cpp:277
static const Def * is_uniform(Defs defs)
Yields defs.front(), if all defs are Check::alpha-equivalent (Mode::Test) and nullptr otherwise.
Definition check.cpp:114
static bool alpha(const Def *d1, const Def *d2)
Definition check.h:96
World & world()
Definition check.h:84
@ Check
In Mode::Check, type inference is happening and Holes will be resolved, if possible.
Definition check.h:89
static const Def * assignable(const Def *type, const Def *value)
Can value be assigned to sth of type?
Definition check.h:103
Base class for all Defs.
Definition def.h:252
bool is_set() const
Yields true if empty or the last op is set.
Definition def.cpp:298
const Def * zonk_mut() const
If mutable, zonk()s all ops and tries to immutabilize it; otherwise just zonk.
Definition check.cpp:23
const Def * proj(nat_t a, nat_t i) const
Similar to World::extract while assuming an arity of a, but also works on Sigmas and Arrays.
Definition def.cpp:593
Defs deps() const noexcept
Definition def.cpp:475
const Def * zonk() const
If Holes have been filled, reconstruct the program without them.
Definition check.cpp:21
World & world() const noexcept
Definition def.cpp:444
virtual const Def * check()
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Definition def.h:596
constexpr auto ops() const noexcept
Definition def.h:306
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:498
const Def * op(size_t i) const noexcept
Definition def.h:309
std::pair< D *, const Var * > isa_binder() const
Is this a mutable that introduces a Var?
Definition def.h:444
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:430
const Def * unfold_type() const
Yields the type of this Def and builds a new Type (UInc n) if necessary.
Definition def.cpp:457
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
Definition def.cpp:332
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.cpp:452
bool has_dep() const
Definition def.h:358
Loc loc() const
Definition def.h:519
bool needs_zonk() const
Yields true, if Def::local_muts() contain a Hole that is set.
Definition check.cpp:12
Error & error(Loc loc, const char *s, Args &&... args)
Definition dbg.h:72
Error & note(Loc loc, const char *s, Args &&... args)
Definition dbg.h:74
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:14
std::pair< Hole *, const Def * > find()
Transitively walks up Holes until the last one while path-compressing everything.
Definition check.cpp:50
Hole * set(const Def *op)
Definition check.h:33
const Def * tuplefy(nat_t)
If unset, explode to Tuple.
Definition check.cpp:75
static const Def * isa_set(const Def *def)
Definition check.h:48
const Def * filter() const
Definition lam.h:122
const Pi * type() const
Definition lam.h:130
const Def * body() const
Definition lam.h:123
const Def * codom() const
Definition lam.h:132
const Def * check() final
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Definition check.cpp:324
static const Def * infer(const Def *dom, const Def *codom)
Definition check.cpp:317
const Def * dom() const
Definition lam.h:35
const Def * codom() const
Definition lam.h:36
Base class for Sigma and Tuple.
Definition tuple.h:10
Def(World *, Node, const Def *type, Defs ops, flags_t flags)
Constructor for an immutable Def.
Definition def.cpp:24
const Def * meta_type() const
Definition rule.h:15
static const Def * infer(const Def *meta_type)
Definition check.cpp:354
const Def * check() override
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Definition check.cpp:347
const Def * lhs() const
Definition rule.h:50
const Def * guard() const
Definition rule.h:52
const Def * rhs() const
Definition rule.h:51
const Def * check() override
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Definition check.cpp:356
const Reform * type() const
Definition rule.h:49
Base class for Arr and Pack.
Definition tuple.h:84
const Def * body() const
Definition tuple.h:91
Def(World *, Node, const Def *type, Defs ops, flags_t flags)
Constructor for an immutable Def.
Definition def.cpp:24
A dependent tuple type.
Definition tuple.h:20
friend class World
Definition tuple.h:63
const Def * check() final
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Definition check.cpp:300
static const Def * infer(World &, Defs)
Definition check.cpp:291
friend class World
Definition tuple.h:80
static const Def * infer(World &, Defs)
Definition check.cpp:284
@ Kind
Definition def.h:764
Extends Rewriter for variable substitution.
Definition rewrite.h:113
Zonker & zonker()
Definition world.h:91
const Def * rewire_mut(Def *)
Definition rewrite.cpp:248
const Def * rewrite(const Def *) final
Definition rewrite.cpp:239
Definition ast.h:14
View< const Def * > Defs
Definition def.h:77
u64 nat_t
Definition types.h:44
Vector< const Def * > DefVec
Definition def.h:78
@ Var
Definition def.h:127
@ Hole
Definition def.h:128
void error(Loc loc, const char *f, Args &&... args)
Definition dbg.h:125
auto elems(std::ostream &os, const auto &range)
Wrap all elements in range through os << T(elem).
Definition print.h:97
TExt< true > Top
Definition lattice.h:172
@ Global
Definition def.h:115
@ Var
Definition def.h:115
@ Hole
Definition def.h:115
@ Uniq
Definition def.h:115
@ UMax
Definition def.h:115