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 "mim/rewrite.h"
4#include "mim/world.h"
5
6#include "fe/assert.h"
7
8namespace mim {
9
10namespace {
11
12class Zonker : public Rewriter {
13public:
14 Zonker(World& world)
15 : Rewriter(world) {}
16
17 Ref rewrite(Ref old_def) override { return (old_def->has_dep(Dep::Infer)) ? Rewriter::rewrite(old_def) : old_def; }
18 Ref rewrite_mut(Def* mut) override { return mut; }
19};
20
21} // namespace
22
23const Def* Def::zonk() const { return has_dep(Dep::Infer) ? *Zonker(world()).rewrite(this) : this; }
24
25/*
26 * Infer
27 */
28
29const Def* Ref::refer(const Def* def) { return def ? Infer::find(def) : nullptr; }
30
31const Def* Infer::find(const Def* def) {
32 // find root
33 auto res = def;
34 for (auto infer = res->isa_mut<Infer>(); infer && infer->op(); infer = res->isa_mut<Infer>()) res = infer->op();
35 // TODO don't re-update last infer
36
37 // path compression: set all Infers along the chain to res
38 for (auto infer = def->isa_mut<Infer>(); infer && infer->op(); infer = def->isa_mut<Infer>()) {
39 def = infer->op();
40 infer->reset(res);
41 }
42
43 assert((!res->isa<Infer>() || res != res->op(0)) && "an Infer shouldn't point to itself");
44 return res;
45}
46
48 if (auto a = type()->isa_lit_arity(); a && !is_set()) {
49 auto n = *a;
50 auto infers = DefVec(n);
51 if (auto sigma = type()->isa_mut<Sigma>(); sigma && n >= 1 && sigma->has_var()) {
52 auto var = sigma->has_var();
53 auto rw = VarRewriter(var, this);
54 infers[0] = world().mut_infer(sigma->op(0));
55 for (size_t i = 1; i != n; ++i) {
56 rw.map(sigma->var(n, i - 1), infers[i - 1]);
57 infers[i] = world().mut_infer(rw.rewrite(sigma->op(i)));
58 }
59 } else {
60 for (size_t i = 0; i != n; ++i) infers[i] = world().mut_infer(type()->proj(n, i));
61 }
62
63 auto tuple = world().tuple(infers);
64 set(tuple);
65 return tuple;
66 }
67 return this;
68}
69
70/*
71 * Check
72 */
73
74#ifdef MIM_ENABLE_CHECKS
75template<Checker::Mode mode> bool Checker::fail() {
76 if (mode == Check && world().flags().break_on_alpha) fe::breakpoint();
77 return false;
78}
79
80Ref Checker::fail() {
81 if (world().flags().break_on_alpha) fe::breakpoint();
82 return {};
83}
84#endif
85
86template<Checker::Mode mode> bool Checker::alpha_(Ref r1, Ref r2) {
87 auto d1 = *r1; // find
88 auto d2 = *r2; // find
89
90 if (!d1 && !d2) return true;
91 if (!d1 || !d2) return fail<mode>();
92
93 // It is only safe to check for pointer equality if there are no Vars involved.
94 // Otherwise, we have to look more thoroughly.
95 // Example: λx.x - λz.x
96 if (!d1->has_dep(Dep::Var) && !d2->has_dep(Dep::Var) && d1 == d2) return true;
97
98 auto i1 = d1->isa_mut<Infer>();
99 auto i2 = d2->isa_mut<Infer>();
100
101 if ((!i1 && !d1->is_set()) || (!i2 && !d2->is_set())) return fail<mode>();
102
103 if (mode == Check) {
104 if (i1 && i2) {
105 // union by rank
106 if (i1->rank() < i2->rank()) std::swap(i1, i2); // make sure i1 is heavier or equal
107 i2->set(i1); // make i1 new root
108 if (i1->rank() == i2->rank()) ++i1->rank();
109 return true;
110 } else if (i1) {
111 i1->set(d2);
112 return true;
113 } else if (i2) {
114 i2->set(d1);
115 return true;
116 }
117 }
118
119 auto mut1 = d1->isa_mut();
120 auto mut2 = d2->isa_mut();
121 if (mut1 && mut2 && mut1 == mut2) return true;
122 // Globals are HACKs and require additionaly HACKs:
123 // Unless they are pointer equal (above) always consider them unequal.
124 if (d1->isa<Global>() || d2->isa<Global>()) return false;
125
126 if (mut1) {
127 if (auto [i, ins] = binders_.emplace(mut1, d2); !ins) return i->second == d2;
128 }
129 if (mut2) {
130 if (auto [i, ins] = binders_.emplace(mut2, d1); !ins) return i->second == d1;
131 }
132
133 // normalize:
134 if ((d1->isa<Lit>() && !d2->isa<Lit>()) // Lit to right
135 || (!d1->isa<UMax>() && d2->isa<UMax>()) // UMax to left
136 || (!d1->isa<Extract>() && d2->isa<Extract>())) // Extract to left
137 std::swap(d1, d2);
138
139 return alpha_internal<mode>(d1, d2);
140}
141
142template<Checker::Mode mode> bool Checker::alpha_internal(Ref d1, Ref d2) {
143 if (!alpha_<mode>(d1->type(), d2->type())) return fail<mode>();
144 if (d1->isa<Top>() || d2->isa<Top>()) return mode == Check;
145 if (mode == Test && (d1->isa_mut<Infer>() || d2->isa_mut<Infer>())) return fail<mode>();
146 if (!alpha_<mode>(d1->arity(), d2->arity())) return fail<mode>();
147
148 if (auto ts = d1->isa<Tuple, Sigma>()) {
149 size_t a = ts->num_ops();
150 for (size_t i = 0; i != a; ++i)
151 if (!alpha_<mode>(ts->op(i), d2->proj(a, i))) return fail<mode>();
152 return true;
153 } else if (auto pa = d1->isa<Pack, Arr>()) {
154 if (pa->node() == d2->node()) return alpha_<mode>(pa->ops().back(), d2->ops().back());
155 if (auto a = pa->isa_lit_arity()) {
156 for (size_t i = 0; i != *a; ++i)
157 if (!alpha_<mode>(pa->proj(*a, i), d2->proj(*a, i))) return fail<mode>();
158 return true;
159 }
160 } else if (auto umax = d1->isa<UMax>(); umax && umax->has_dep(Dep::Infer) && !d2->isa<UMax>()) {
161 // .umax(a, ?) == x => .umax(a, x)
162 for (auto op : umax->ops())
163 if (auto inf = op->isa_mut<Infer>(); inf && !inf->is_set()) inf->set(d2);
164 d1 = umax->rebuild(umax->type(), umax->ops());
165 }
166
167 if (d1->node() != d2->node() || d1->flags() != d2->flags() || d1->num_ops() != d2->num_ops()) return fail<mode>();
168
169 if (auto var1 = d1->isa<Var>()) {
170 auto var2 = d2->as<Var>();
171 if (auto i = binders_.find(var1->mut()); i != binders_.end()) return i->second == var2->mut();
172 if (auto i = binders_.find(var2->mut()); i != binders_.end()) return fail<mode>(); // var2 is bound
173 // both var1 and var2 are free: OK, when they are the same or in Check mode
174 return var1 == var2 || mode == Check;
175 }
176
177 for (size_t i = 0, e = d1->num_ops(); i != e; ++i)
178 if (!alpha_<mode>(d1->op(i), d2->op(i))) return fail<mode>();
179 return true;
180}
181
182Ref Checker::assignable_(Ref type, Ref val) {
183 auto val_ty = Ref::refer(val->type());
184 if (type == val_ty) return val;
185
186 if (auto sigma = type->isa<Sigma>()) {
187 if (!alpha_<Check>(type->arity(), val_ty->arity())) return fail();
188
189 size_t a = sigma->num_ops();
190 auto red = sigma->reduce(val);
191 auto new_ops = DefVec(red.size());
192 for (size_t i = 0; i != a; ++i) {
193 auto new_val = assignable_(red[i], val->proj(a, i));
194 if (new_val)
195 new_ops[i] = new_val;
196 else
197 return fail();
198 }
199 return world().tuple(new_ops);
200 } else if (auto arr = type->isa<Arr>()) {
201 if (!alpha_<Check>(type->arity(), val_ty->arity())) return fail();
202
203 // TODO ack sclarize threshold
204 if (auto a = Lit::isa(arr->arity())) {
205 auto new_ops = DefVec(*a);
206 for (size_t i = 0; i != *a; ++i) {
207 auto new_val = assignable_(arr->proj(*a, i), val->proj(*a, i));
208 if (new_val)
209 new_ops[i] = new_val;
210 else
211 return fail();
212 }
213 return world().tuple(new_ops);
214 }
215 } else if (auto vel = val->isa<Vel>()) {
216 if (auto new_val = assignable_(type, vel->value())) return world().vel(type, new_val);
217 return fail();
218 } else if (auto uniq = val->type()->isa<Uniq>()) {
219 if (auto new_val = assignable(type, uniq->inhabitant())) return new_val;
220 return fail();
221 }
222
223 return alpha_<Check>(type, val_ty) ? val : fail();
224}
225
227 if (defs.empty()) return nullptr;
228 auto first = defs.front();
229 for (size_t i = 1, e = defs.size(); i != e; ++i)
230 if (!alpha<Test>(first, defs[i])) return nullptr;
231 return first;
232}
233
234/*
235 * infer & check
236 */
237
238Ref Arr::check(size_t, Ref def) { return def; } // TODO
239
241 auto t = body()->unfold_type();
243 error(type()->loc(), "declared sort '{}' of array does not match inferred one '{}'", type(), t);
244 return t;
245}
246
248 if (ops.size() == 0) return w.type<1>();
249 auto kinds = DefVec(ops.size(), [ops](size_t i) { return ops[i]->unfold_type(); });
250 return w.umax<Sort::Kind>(kinds);
251}
252
253Ref Sigma::check(size_t, Ref def) { return def; } // TODO
254
256 auto t = infer(world(), ops());
257 if (*t != *type()) {
258 // TODO HACK
260 return t;
261 else {
262 world().WLOG(
263 "incorrect type '{}' for '{}'. Correct one would be: '{}'. I'll keep this one nevertheless due to "
264 "bugs in clos-conv",
265 type(), this, t);
266 return type();
267 }
268 }
269 return t;
270}
271
272Ref Lam::check(size_t i, Ref def) {
273 if (i == 0) {
274 if (auto filter = Checker::assignable(world().type_bool(), def)) return filter;
275 throw Error().error(filter()->loc(), "filter '{}' of lambda is of type '{}' but must be of type 'Bool'",
276 filter(), filter()->type());
277 } else if (i == 1) {
278 if (auto body = Checker::assignable(codom(), def)) return body;
279 throw Error()
280 .error(def->loc(), "body of function is not assignable to declared codomain")
281 .note(def->loc(), "body: '{}'", def)
282 .note(def->loc(), "type: '{}'", def->type())
283 .note(codom()->loc(), "codomain: '{}'", codom());
284 }
285 fe::unreachable();
286}
287
288Ref Pi::infer(Ref dom, Ref codom) {
289 auto& w = dom->world();
290 return w.umax<Sort::Kind>({dom->unfold_type(), codom->unfold_type()});
291}
292
293Ref Pi::check(size_t, Ref def) { return def; }
294
296 auto t = infer(dom(), codom());
298 error(type()->loc(), "declared sort '{}' of function type does not match inferred one '{}'", type(), t);
299 return t;
300}
301
302#ifndef DOXYGEN
303template bool Checker::alpha_<Checker::Check>(Ref, Ref);
304template bool Checker::alpha_<Checker::Test>(Ref, Ref);
305#endif
306
307} // namespace mim
Ref check() override
Definition check.cpp:240
Ref body() const
Definition tuple.h:78
World & world()
Definition check.h:54
@ Test
In Mode::Test, no type inference is happening and Infers will not be touched.
Definition check.h:62
@ Check
In Mode::Check, type inference is happening and Infers will be resolved, if possible.
Definition check.h:59
static Ref is_uniform(Defs defs)
Yields defs.front(), if all defs are Check::alpha-equivalent (Mode::Test) and nullptr otherwise.
Definition check.cpp:226
static bool alpha(Ref d1, Ref d2)
Definition check.h:65
static Ref assignable(Ref type, Ref value)
Can value be assigned to sth of type?
Definition check.h:69
Base class for all Defs.
Definition def.h:212
bool is_set() const
Yields true if empty or the last op is set.
Definition def.cpp:281
Ref op(size_t i) const noexcept
Definition def.h:264
Ref var()
Not necessarily a Var: E.g., if the return type is [], this will yield ().
Definition def.cpp:423
const Def * zonk() const
Definition check.cpp:23
World & world() const noexcept
Definition def.cpp:384
constexpr auto ops() const noexcept
Definition def.h:261
Ref unfold_type() const
Yields the type of this Def and builds a new Type (UInc n) if necessary.
Definition def.cpp:390
virtual Ref check()
Definition def.h:517
T * isa_mut() const
If this is *mut*able, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:432
Def * reset(size_t i, Ref def)
Successively reset from left to right.
Definition def.h:286
Ref type() const noexcept
Yields the raw type of this Def, i.e. maybe nullptr.
Definition def.h:241
bool has_dep() const
Definition def.h:313
Loc loc() const
Definition def.h:452
std::optional< nat_t > isa_lit_arity() const
Definition def.cpp:457
Ref 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:487
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
Definition def.h:385
Error & error(Loc loc, const char *s, Args &&... args)
Definition dbg.h:71
Error & note(Loc loc, const char *s, Args &&... args)
Definition dbg.h:73
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:10
Ref tuplefy()
If unset, explode to Tuple.
Definition check.cpp:47
static const Def * find(const Def *)
Union-Find to unify Infer nodes.
Definition check.cpp:31
const Def * op() const
Definition check.h:20
Infer * set(const Def *op)
Definition check.h:21
Ref filter() const
Definition lam.h:117
Ref codom() const
Definition lam.h:127
const Pi * type() const
Definition lam.h:125
Ref body() const
Definition lam.h:118
static std::optional< T > isa(Ref def)
Definition def.h:731
Ref codom() const
Definition lam.h:33
static Ref infer(Ref dom, Ref codom)
Definition check.cpp:288
Ref dom() const
Definition lam.h:32
Ref check() override
Definition check.cpp:295
Helper class to retrieve Infer::arg if present.
Definition def.h:86
const Def * def() const
Retrieve wrapped Def without Infer::refering.
Definition def.h:97
static const Def * refer(const Def *def)
Same as Infer::find but does nothing if def is nullptr.
Definition check.cpp:29
virtual Ref rewrite(Ref)
Definition rewrite.cpp:9
static Ref infer(World &, Defs)
Definition check.cpp:247
Ref check() override
Definition check.cpp:255
This is a thin wrapper for std::span<T, N> with the following additional features:
Definition span.h:28
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:33
Infer * mut_infer(Ref type)
Definition world.h:229
Ref tuple(Defs ops)
Definition world.cpp:231
Ref vel(Ref type, Ref value)
Definition world.cpp:506
Ref op(trait o, Ref type)
Definition core.h:33
Definition ast.h:14
Vector< const Def * > DefVec
Definition def.h:62
void error(Loc loc, const char *f, Args &&... args)
Definition dbg.h:122
TExt< true > Top
Definition lattice.h:178