MimIR
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 auto& memo = memo_[mode];
152 if (memo.contains({d1, d2}) || memo.contains({d2, d1})) return true;
153 if (!alpha_impl_<mode>(d1, d2)) return false;
154 memo.emplace(d1, d2);
155 return true;
156}
157
158template<Checker::Mode mode>
159bool Checker::alpha_impl_(const Def* d1, const Def* d2) {
160 for (bool todo = true; todo;) {
161 // below we check type and arity which may in turn open up more opportunities for zonking
162 todo = false;
163 d1 = d1->zonk_mut();
164 d2 = d2->zonk_mut();
165
166 // It is only safe to check for pointer equality if there are no Vars involved.
167 // Otherwise, we have to look more thoroughly.
168 // Example: λx.x - λz.x
169 if (!d1->has_dep(Dep::Var) && !d2->has_dep(Dep::Var) && d1 == d2) return true;
170
171 auto h1 = d1->isa_mut<Hole>();
172 auto h2 = d2->isa_mut<Hole>();
173
174 if constexpr (mode == Check) {
175 if (h1) return h1->set(d2), true;
176 if (h2) return h2->set(d1), true;
177 } else if (h1 || h2) // mode == Test and h1 or h2 is an unresolved Hole
178 return fail<Test>();
179
180 if (!d1->is_set() || !d2->is_set()) return fail<mode>();
181
182 auto mut1 = d1->isa_mut();
183 auto mut2 = d2->isa_mut();
184
185 if (mut1 && mut2 && mut1 == mut2) return true;
186
187 // Globals are HACKs and require additionaly HACKs:
188 // Unless they are pointer equal (above) always consider them unequal.
189 if (d1->isa<Global>() || d2->isa<Global>()) return false;
190
191 if (auto [i, ins] = bind(mut1, d2); !ins) return i->second == d2;
192 if (auto [i, ins] = bind(mut2, d1); !ins) return i->second == d1;
193
194 if (d1->isa<Top>() || d2->isa<Top>()) return mode == Check;
195
196 if (auto t1 = d1->type()) {
197 if (auto t2 = d2->type()) {
198 if (!alpha_<mode>(t1, t2)) return fail<mode>();
199 }
200 }
201
202 if (!alpha_<mode>(d1->arity(), d2->arity())) return fail<mode>();
203
204 auto new_d1 = d1->zonk_mut();
205 auto new_d2 = d2->zonk_mut();
206 if (new_d1 != d1 || new_d2 != d2) {
207 todo = true;
208 d1 = new_d1;
209 d2 = new_d2;
210 }
211 }
212
213 auto seq1 = d1->isa<Seq>();
214 auto seq2 = d2->isa<Seq>();
215
216 if constexpr (mode == Mode::Check) {
217 if (auto umax = d1->isa<UMax>(); umax && !d2->isa<UMax>()) return check(umax, d2);
218 if (auto umax = d2->isa<UMax>(); umax && !d1->isa<UMax>()) return check(umax, d1);
219
220 if (seq1 && seq1->arity() == world().lit_nat_1() && !seq2) return check1(seq1, d2);
221 if (seq2 && seq2->arity() == world().lit_nat_1() && !seq1) return check1(seq2, d1);
222
223 if (seq1 && seq2) {
224 if (auto mut_seq = seq1->isa_mut<Seq>(); mut_seq && seq2->isa_imm()) return check(mut_seq, seq2);
225 if (auto mut_seq = seq2->isa_mut<Seq>(); mut_seq && seq1->isa_imm()) return check(mut_seq, seq1);
226 }
227 }
228
229 if (auto prod = d1->isa<Prod>()) return check<mode>(prod, d2);
230 if (auto prod = d2->isa<Prod>()) return check<mode>(prod, d1);
231 if (seq1 && seq2) return alpha_<mode>(seq1->body(), seq2->body());
232
233 if (d1->node() != d2->node() || d1->flags() != d2->flags()) return fail<mode>();
234
235 if (auto var1 = d1->isa<Var>()) {
236 auto var2 = d2->as<Var>();
237 if (auto i = binders_.find(var1->binder()); i != binders_.end()) return i->second == var2->binder();
238 if (auto i = binders_.find(var2->binder()); i != binders_.end()) return fail<mode>(); // var2 is bound
239 // both var1 and var2 are free: OK, when they are the same or in Check mode
240 return var1 == var2 || mode == Check;
241 }
242
243 for (size_t i = 0, e = d1->num_ops(); i != e; ++i)
244 if (!alpha_<mode>(d1->op(i), d2->op(i))) return fail<mode>();
245 return true;
246}
247
248template<Checker::Mode mode>
249bool Checker::check(const Prod* prod, const Def* def) {
250 size_t a = prod->num_ops();
251 for (size_t i = 0; i != a; ++i)
252 if (!alpha_<mode>(prod->op(i), def->proj(a, i))) return fail<mode>();
253 return true;
254}
255
256// alpha(«1; body», def) -> alpha(body, def);
257bool Checker::check1(const Seq* seq, const Def* def) {
258 auto body = seq->reduce(world().lit_idx_1_0()); // try to get rid of var inside of body
259 if (!alpha_<Check>(body, def)) return fail<Check>();
260 if (auto mut_seq = seq->isa_mut<Seq>()) mut_seq->set(world().lit_nat_1(), body->zonk());
261 return true;
262}
263
264// Try to get rid of mut_seq's var: it may occur in its body and vanish after reduction
265// as holes might have been filled in the meantime.
266bool Checker::check(Seq* mut_seq, const Seq* imm_seq) {
267 auto mut_body = mut_seq->reduce(world().top(world().type_idx(mut_seq->arity())));
268 if (!alpha_<Check>(mut_body, imm_seq->body())) return fail<Check>();
269
270 mut_seq->set(mut_seq->arity(), mut_body->zonk());
271 return true;
272}
273
274bool Checker::check(const UMax* umax, const Def* def) {
275 for (auto op : umax->ops())
276 if (!alpha<Check>(op, def)) return fail<Check>();
277 return true;
278}
279
280/*
281 * infer & check
282 */
283
284const Def* Arr::check(size_t, const Def* def) { return def; } // TODO
285
286const Def* Arr::check() {
287 auto t = body()->unfold_type();
289 error(type()->loc(), "declared sort '{}' of array does not match inferred one '{}'", type(), t);
290 return t;
291}
292
294 auto elems = absl::FixedArray<const Def*>(ops.size());
295 for (size_t i = 0, e = ops.size(); i != e; ++i)
296 elems[i] = ops[i]->unfold_type();
297 return world.sigma(elems);
298}
299
301 auto elems = absl::FixedArray<const Def*>(ops.size());
302 for (size_t i = 0, e = ops.size(); i != e; ++i)
303 elems[i] = ops[i]->unfold_type();
304 return w.umax<UMax::Kind>(elems);
305}
306
307const Def* Sigma::check(size_t, const Def* def) { return def; } // TODO
308
310 auto t = infer(world(), ops());
311 if (t != type()) {
312 // TODO HACK
314 return t;
315 else {
316 world().WLOG(
317 "incorrect type '{}' for '{}'. Correct one would be: '{}'. I'll keep this one nevertheless due to "
318 "bugs in clos-conv",
319 type(), this, t);
320 return type();
321 }
322 }
323 return t;
324}
325
326const Def* Pi::infer(const Def* dom, const Def* codom) {
327 auto& w = dom->world();
328 return w.umax<UMax::Kind>({dom->unfold_type(), codom->unfold_type()});
329}
330
331const Def* Pi::check(size_t, const Def* def) { return def; }
332
333const Def* Pi::check() {
334 auto t = infer(dom(), codom());
336 error(type()->loc(), "declared sort '{}' of function type does not match inferred one '{}'", type(), t);
337 return t;
338}
339
340const Def* Lam::check(size_t i, const Def* def) {
341 if (i == 0) {
342 if (auto filter = Checker::assignable(world().type_bool(), def)) return filter;
343 throw Error().error(filter()->loc(), "filter '{}' of lambda is of type '{}' but must be of type 'Bool'",
344 filter(), filter()->type());
345 }
346 assert(i == 1);
347 if (auto body = Checker::assignable(codom(), def)) return body;
348 throw Error()
349 .error(def->loc(), "body of function is not assignable to declared codomain")
350 .note(def->loc(), "body: '{}'", def)
351 .note(def->loc(), "type: '{}'", def->type())
352 .note(codom()->loc(), "codomain: '{}'", codom());
353}
354
355const Def* Reform::check() {
356 auto t = infer(dom());
358 error(type()->loc(), "declared sort '{}' of rule type does not match inferred one '{}'", type(), t);
359 return t;
360}
361
362const Def* Reform::infer(const Def* dom) { return dom->unfold_type(); }
363
364const Def* Rule::check() {
365 auto t1 = lhs()->type();
366 auto t2 = rhs()->type();
368 error(type()->loc(), "type mismatch: '{}' for lhs, but '{}' for rhs", t1, t2);
369 if (!Checker::assignable(world().type_bool(), guard()))
370 error(guard()->loc(), "condition '{}' of rewrite is of type '{}' but must be of type 'Bool'", guard(),
371 guard()->type());
372
373 return type();
374}
375
376const Def* Rule::check(size_t, const Def* def) {
377 return def;
378 // TODO: do actual check + what are the parameters ?
379}
380
381#ifndef DOXYGEN
382template bool Checker::alpha_<Checker::Check>(const Def*, const Def*);
383template bool Checker::alpha_<Checker::Test>(const Def*, const Def*);
384#endif
385
386} // 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:286
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:98
World & world()
Definition check.h:86
@ Check
In Mode::Check, type inference is happening and Holes will be resolved, if possible.
Definition check.h:91
static const Def * assignable(const Def *type, const Def *value)
Can value be assigned to sth of type?
Definition check.h:105
Base class for all Defs.
Definition def.h:261
bool is_set() const
Yields true if empty or the last op is set.
Definition def.cpp:308
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:635
bool has_dep() const noexcept
Definition def.h:370
Defs deps() const noexcept
Definition def.cpp:514
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:483
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:634
constexpr auto ops() const noexcept
Definition def.h:317
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:527
const Def * op(size_t i) const noexcept
Definition def.h:320
std::pair< D *, const Var * > isa_binder() const
Is this a mutable that introduces a Var?
Definition def.h:455
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:441
const Def * unfold_type() const
Yields the type of this Def and builds a new Type (UInc n) if necessary.
Definition def.cpp:496
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
Definition def.cpp:342
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.cpp:491
Loc loc() const
Definition def.h:557
bool needs_zonk() const
Yields true, if Def::local_muts() contain a Hole that is set.
Definition check.cpp:12
Error & error(Loc loc, std::format_string< Args... > s, Args &&... args)
Definition dbg.h:63
Error & note(Loc loc, std::format_string< Args... > s, Args &&... args)
Definition dbg.h:65
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:16
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:35
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:50
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:333
static const Def * infer(const Def *dom, const Def *codom)
Definition check.cpp:326
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 * 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:355
static const Def * infer(const Def *dom)
Definition check.cpp:362
const Def * dom() const
Definition rule.h:18
const Def * lhs() const
Definition rule.h:62
const Def * guard() const
Definition rule.h:64
const Def * rhs() const
Definition rule.h:63
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:364
const Reform * type() const
Definition rule.h:54
Base class for Arr and Pack.
Definition tuple.h:86
const Def * body() const
Definition tuple.h:95
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:22
friend class World
Definition tuple.h:65
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:309
static const Def * infer(World &, Defs)
Definition check.cpp:300
friend class World
Definition tuple.h:82
static const Def * infer(World &, Defs)
Definition check.cpp:293
@ Kind
Definition def.h:799
Extends Rewriter for variable substitution.
Definition rewrite.h:107
Zonker & zonker()
Definition world.h:95
const Def * rewire_mut(Def *)
Definition rewrite.cpp:354
const Def * rewrite(const Def *) final
Definition rewrite.cpp:345
Definition ast.h:14
View< const Def * > Defs
Definition def.h:78
u64 nat_t
Definition types.h:37
Vector< const Def * > DefVec
Definition def.h:79
@ Var
Depends on a Var.
Definition def.h:123
@ Hole
Depends on a Hole.
Definition def.h:124
TExt< true > Top
Definition lattice.h:177
void error(Loc loc, std::format_string< Args... > f, Args &&... args)
Definition dbg.h:114
@ Global
Definition def.h:109
@ Var
Definition def.h:109
@ Hole
Definition def.h:109
@ Uniq
Definition def.h:109
@ UMax
Definition def.h:109