MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
world.cpp
Go to the documentation of this file.
1#include "mim/world.h"
2
3#include <ranges>
4
5#include <fe/container.h>
6#include <fe/worklist.h>
7
8#include "mim/check.h"
9#include "mim/def.h"
10#include "mim/driver.h"
11#include "mim/rewrite.h"
12#include "mim/schedule.h"
13#include "mim/tuple.h"
14
15#include "mim/util/gid.h"
16
17namespace mim {
18
19namespace {
20
21bool is_shape(const Def* s) {
22 if (s->isa<Nat>()) return true;
23 if (auto arr = s->isa<Arr>()) return arr->body()->zonk()->isa<Nat>();
24 if (auto sig = s->isa_imm<Sigma>())
25 return std::ranges::all_of(sig->ops(), [](const Def* op) { return op->isa<Nat>(); });
26
27 return false;
28}
29
30/// Is @p def an `Idx` - or an aggregate of `Idx`%s, i.e. a multi-dimensional index?
31bool isa_indices(const Def* def) {
32 if (!def) return false; // Univ has no type
33 if (Idx::isa(def)) return true;
34 if (auto sigma = def->isa<Sigma>()) return std::ranges::all_of(sigma->ops(), [](auto op) { return Idx::isa(op); });
35 if (auto arr = def->isa<Arr>()) return Idx::isa(arr->body());
36 return false;
37}
38
39/// Sorts by gid and drops duplicates; Def%s are hash-consed, so pointer identity *is* structural identity.
40void sort_unique(DefVec& defs) {
41 std::ranges::sort(defs, GIDLt<const Def*>());
42 defs.erase(std::unique(defs.begin(), defs.end()), defs.end());
43}
44
45} // namespace
46
48 assert(!def->is_external());
49 assert(def->is_closed());
50 def->external_ = true;
51 fe::assert_emplace(sym2mut_, def->sym(), def);
52}
53
55 assert(def->is_external());
56 def->external_ = false;
57 auto num = sym2mut_.erase(def->sym());
58 assert_unused(num == 1);
59}
60
61const Def* World::Annexes::attach(flags_t flags, Sym sym, const Def* def) {
62 driver().log().t("register annex `{}` 0x{:x} → {}", sym, flags, def);
63 if (driver().is_loaded(Annex::demangle(flags))) {
64 fe::assert_emplace(flags2entry_, flags, Annexes::Entry{sym, def});
65 fe::assert_emplace(sym2flags_, sym, flags);
66 def->annex_ = true;
67 return def;
68 }
69 return nullptr;
70}
71
73 if (!driver().is_loaded(Annex::demangle(flags))) return;
74 // An alias spelled the same as its target's own (unqualified) name registers the identical
75 // qualified string as the target - a benign no-op, not a conflict.
76 if (auto [i, ins] = sym2flags_.try_emplace(sym, flags); !ins) assert(i->second == flags);
77}
78
79/*
80 * constructor & destructor
81 */
82
83#if (!defined(_MSC_VER) && defined(NDEBUG))
84bool World::Lock::guard_ = false;
85#endif
86
88 : driver_(driver)
89 , zonker_(*this)
90 , state_(state)
91 , move_(driver) {
92 data_.univ = insert<Univ>(*this);
93 data_.lit_univ_0 = lit_univ(0);
94 data_.lit_univ_1 = lit_univ(1);
95 data_.type_0 = type(lit_univ_0());
96 data_.type_1 = type(lit_univ_1());
97 data_.type_bot = insert<Bot>(type());
98 data_.type_top = insert<Top>(type());
99 data_.sigma = unify<Sigma>(type(), Defs{})->as<Sigma>();
100 data_.tuple = unify<Tuple>(sigma(), Defs{})->as<Tuple>();
101 data_.type_nat = insert<mim::Nat>(*this);
102 data_.type_idx = insert<mim::Idx>(pi(type_nat(), type()));
103 data_.top_nat = insert<Top>(type_nat());
104 data_.lit_nat_0 = lit_nat(0);
105 data_.lit_nat_1 = lit_nat(1);
106 data_.lit_idx_1_0 = lit_idx(1, 0);
107 data_.type_bool = type_idx(2);
108 data_.lit_bool[0] = lit_idx(2, 0_u64);
109 data_.lit_bool[1] = lit_idx(2, 1_u64);
110 data_.lit_nat_max = lit_nat(nat_t(-1));
111}
112
115
116// ~Def() has nothing to do, so World does not run it.
117World::~World() = default;
118
119static_assert(std::is_trivially_destructible_v<Dbg> && std::is_trivially_destructible_v<Vars>
120 && std::is_trivially_destructible_v<Muts> && std::is_trivially_destructible_v<NormalizeFn>,
121 "a Def member gained a non-trivial destructor: World::~World must destroy Defs again");
122
123/*
124 * Driver
125 */
126
127fe::Error& World::error() { return driver().error(); }
128const fe::Error& World::error() const { return driver().error(); }
129const fe::Log& World::log() const { return driver().log(); }
130Flags& World::flags() { return driver().flags(); }
131
132Sym World::sym(const char* s) { return driver().sym(s); }
133Sym World::sym(std::string_view s) { return driver().sym(s); }
134Sym World::sym(const std::string& s) { return driver().sym(s); }
135
136/*
137 * factory methods
138 */
139
140const Type* World::type(const Def* level) {
141 if (!level) return nullptr;
142 level = level->zonk();
143
144 if (!level->isa_type<Univ>())
145 level->blame("argument `{}` to `Type` must be of type `Univ` but is of type `{}`", level, type_of(level))
146 .bail();
147
148 return unify<Type>(level)->as<Type>();
149}
150
151const Def* World::uinc(const Def* op, level_t offset) {
152 op = op->zonk();
153
154 if (!op->isa_type<Univ>())
155 op->blame("operand `{}` of a universe increment must be of type `Univ` but is of type `{}`", op, type_of(op))
156 .bail();
157
158 if (auto l = Lit::isa(op)) return lit_univ(*l + 1);
159 return unify<UInc>(op, offset);
160}
161
162static void flatten_umax(DefVec& ops, const Def* def) {
163 if (auto umax = def->isa<UMax>())
164 for (auto op : umax->ops())
165 flatten_umax(ops, op);
166 else
167 ops.emplace_back(def);
168}
169
170template<int sort>
171const Def* World::umax(Defs ops_) {
172 DefVec ops;
173 ops.reserve(ops_.size());
174 for (auto op : ops_) {
175 op = op->zonk();
176
177 // Peel off as many layers as the sort of the incoming ops demands to arrive at a Univ level:
178 // a Univ level is already there, a Kind is a `Type lvl`, a Type needs one unfold, a term two.
179 if constexpr (sort >= UMax::Type) op = op->unfold_type();
180 if constexpr (sort == UMax::Term) op = op->unfold_type();
181 if constexpr (sort >= UMax::Kind) {
182 if (auto type = op->isa<Type>())
183 op = type->level();
184 else
185 op->blame("operand `{}` must be a `Type` of some universe level", op).bail();
186 }
187
188 flatten_umax(ops, op);
189 }
190
191 level_t lvl = 0;
192 DefVec res;
193 res.reserve(ops.size());
194 for (auto op : ops) {
195 if (!op->isa_type<Univ>())
196 op->blame("operand `{}` of a universe max must be of type `Univ` but is of type `{}`", op, type_of(op))
197 .bail();
198
199 if (auto l = Lit::isa(op))
200 lvl = std::max(lvl, *l);
201 else
202 res.emplace_back(op);
203 }
204
205 const Def* l = lit_univ(lvl);
206 if (res.empty()) return sort == UMax::Univ ? l : type(l);
207 if (lvl > 0) res.emplace_back(l);
208
209 sort_unique(res);
210 const Def* umax = unify<UMax>(*this, res);
211 return sort == UMax::Univ ? umax : type(umax);
212}
213
214// TODO more thorough & consistent checks for singleton types
215
216const Def* World::var(Def* mut) {
217 if (auto var = mut->var_) return var;
218
219 if (auto var_type = mut->var_type()) { // could be nullptr, if frozen
220 if (auto s = Idx::isa(var_type)) {
221 if (auto l = Lit::isa(s); l && l == 1) return lit_idx_1_0();
222 } else if (auto s = var_type->isa<Sigma>(); s && s->num_ops() == 0)
223 return tuple(s, {});
224 }
225
226 return mut->var_ = unify<Var>(mut);
227}
228
229template<bool Normalize>
230const Def* World::implicit_app(const Def* callee, const Def* arg) {
231 while (auto pi = Pi::isa_implicit(callee->unfold_type()))
232 callee = app(callee, mut_hole(pi->dom()));
233 return app<Normalize>(callee, arg);
234}
235
236template<bool Normalize>
237const Def* World::app(const Def* callee, const Def* arg) {
238 callee = callee->zonk();
239 arg = arg->zonk();
240
241 auto pi = callee->isa_type<Pi>();
242 if (!pi)
243 callee->blame("callee is not of function type")
244 .n("callee `{}` has type `{}`", callee, type_of(callee))
245 .n(callee->loc(), "callee `{}` declared here", callee)
246 .bail();
247
248 auto new_arg = Checker::assignable(pi->dom(), arg);
249 if (!new_arg)
250 arg->blame("argument is not assignable to callee's domain")
251 .n("expected `{}`, got `{}`", pi->dom(), type_of(arg))
252 .n(callee->loc(), "callee `{}` declared here", callee)
253 .bail();
254
255 // re-zonk after assignable check above - we might have inferred new stuff
256 arg = new_arg->zonk();
257 callee = callee->zonk();
258 pi = callee->isa_type<Pi>();
259
260 // always β-reduce non-recursive, non-parametric lambdas
261 if (auto imm = callee->isa_imm<Lam>()) return imm->body();
262
263 if (auto lam = callee->isa_mut<Lam>(); lam && lam->is_set()) {
264 auto var = lam->has_var();
265
266 // Applying a Lam to its own Var is the identity substitution, so it resolves to the body.
267 // This unfolds a self-application / fixed-point reference.
268 if (var && arg == var) return lam->body();
269
270 // β-reduce or partially evaluate a set, mutable Lam.
271 if (lam->filter() != lit_ff()) {
272 if (!var) {
273 if (lam->filter() == lit_tt()) return lam->body();
274 } else if (auto i = move_.substs.find({var, arg}); i != move_.substs.end()) {
275 // Reuse the cached reduct if its filter held.
276 auto [filter, body] = i->second->defs<2>();
277 if (filter == lit_tt()) return body;
278 } else {
279 // Evaluate the filter; if it holds, reduce the body and cache the reduct.
280 auto rw = VarRewriter(var, arg);
281 auto filter = rw.rewrite(lam->filter());
282 if (filter == lit_tt()) {
283 log().d("partially evaluate {} ({})", lam, arg);
284 auto body = rw.rewrite(lam->body());
285 cache_reduct(var, arg, {filter, body});
286 return body;
287 }
288 }
289 }
290 }
291
292 auto type = pi->reduce(arg)->zonk();
293 callee = callee->zonk();
294 auto [axm, curry, trip] = Axm::next(callee);
295
296 if (axm)
297 if (auto normalizer = axm->normalizer(); Normalize && normalizer && curry == 0)
298 if (auto norm = normalizer(type, callee, arg)) return norm;
299
300 return raw_app(axm, curry, trip, type, callee, arg);
301}
302
303const Def* World::raw_app(const Def* type, const Def* callee, const Def* arg) {
304 type = type->zonk();
305 callee = callee->zonk();
306 arg = arg->zonk();
307
308 auto [axm, curry, trip] = Axm::next(callee);
309 return raw_app(axm, curry, trip, type, callee, arg);
310}
311
312const Def* World::raw_app(const Axm* axm, u8 curry, u8 trip, const Def* type, const Def* callee, const Def* arg) {
313 return unify<App>(axm, curry, trip, type, callee, arg);
314}
315
316const Def* World::sigma(Defs ops) {
317 auto n = ops.size();
318 if (n == 0) return sigma();
319 if (n == 1) return ops[0]->zonk();
320
321 auto zops = Def::zonk(ops);
322 if (auto uni = Checker::is_uniform(zops)) return arr(n, uni);
323 return unify<Sigma>(Sigma::infer(*this, zops), zops);
324}
325
326const Def* World::tuple(Defs ops) {
327 auto n = ops.size();
328 if (n == 0) return tuple();
329 if (n == 1) return ops[0]->zonk();
330
331 auto zops = Def::zonk(ops);
332 auto sigma = Tuple::infer(*this, zops);
333 auto t = tuple(sigma, zops);
334 auto new_t = Checker::assignable(sigma, t);
335 if (!new_t)
336 t->blame("tuple `{}` of type `{}` is not assignable to inferred type `{}`", t, type_of(t), sigma).bail();
337
338 return new_t;
339}
340
341const Def* World::tuple(const Def* type, Defs ops_) {
342 // TODO type-check type vs inferred type
343 type = type->zonk();
344 auto ops = Def::zonk(ops_);
345
346 auto n = ops.size();
347 if (!type->isa_mut<Sigma>()) {
348 if (n == 0) return tuple();
349 if (n == 1) return ops[0];
350 if (auto uni = Checker::is_uniform(ops)) return pack(n, uni);
351 }
352
353 // eta rule for tuples: (extract(tup, 0), extract(tup, 1), extract(tup, 2)) -> tup
354 if (auto ex0 = n != 0 ? ops[0]->isa<Extract>() : nullptr) {
355 auto tup = ex0->tuple();
356 bool eta = tup->type() == type;
357 for (size_t i = 0; i != n && eta; ++i) {
358 auto ex = ops[i]->isa<Extract>();
359 auto id = ex ? Lit::isa(ex->index()) : std::nullopt;
360 eta = ex && id && *id == u64(i) && ex->tuple() == tup;
361 }
362
363 if (eta) return tup;
364 }
365
366 return unify<Tuple>(type, ops);
367}
368
369const Def* World::tuple(Sym sym) {
370 return tuple(DefVec(sym, [this](char c) { return lit_i8(c); }));
371}
372
373const Def* World::extract(const Def* d, const Def* index) {
374 if (!d || !index) return nullptr; // can happen if frozen
375 d = d->zonk();
376 index = index->zonk();
377
378 // The scalar case is by far the most common one, so probe it first and only fall back to the aggregate check.
379 auto index_ty = index->unfold_type();
380 auto size = Idx::isa(index_ty);
381 auto lidx = Lit::isa(index);
382 if (!size && !isa_indices(index_ty))
383 index->blame("index `{}` must be of `Idx` type but is of type `{}`", index, type_of(index)).bail();
384
385 if (auto tuple = index->isa<Tuple>()) {
386 for (auto op : tuple->ops())
387 d = extract(d, op);
388 return d;
389 } else if (auto pack = index->isa<Pack>()) {
390 if (auto a = Lit::isa(index->arity())) {
391 for (nat_t i = 0, e = *a; i != e; ++i) {
392 auto idx = pack->has_var() ? pack->reduce(lit_idx(*a, i)) : pack->body();
393 d = extract(d, idx);
394 }
395 return d;
396 }
397 }
398
399 auto type = d->unfold_type();
400
401 if (size) {
402 if (auto l = Lit::isa(size); l && *l == 1) {
403 if (!lidx || *lidx != 0) log().w("index of `Idx 1` is not the literal 0: {}", index);
404 // A *mutable* Sigma may be a 1-tuple and still needs a real Extract; TODO mutable Arr?
405 auto sigma = type->isa_mut<Sigma>();
406 if (!sigma || sigma->num_ops() != 1) return d;
407 }
408 }
409
410 if (size && !Checker::alpha<Checker::Check>(type->arity(), size))
411 index->blame("index `{}` does not fit within arity `{}`", index, type->arity()).bail();
412 // TODO if we have indices we need to check as well that this is compatible with `d`
413
414 if (auto pack = d->isa<Pack>()) {
415 if (pack->has_var())
416 return pack->reduce(index);
417 else
418 return pack->body();
419 }
420
421 // extract(insert(x, index, val), index) -> val
422 if (auto insert = d->isa<Insert>()) {
423 if (index == insert->index()) return insert->value();
424 }
425
426 if (lidx) {
427 if (auto hole = d->isa_mut<Hole>()) d = hole->tuplefy(Idx::as_lit(index_ty));
428 if (auto tuple = d->isa<Tuple>()) return tuple->op(*lidx);
429
430 // extract(insert(x, j, val), i) -> extract(x, i) where i != j (guaranteed by rule above)
431 if (auto insert = d->isa<Insert>()) {
432 if (insert->index()->isa<Lit>()) return extract(insert->tuple(), index);
433 }
434
435 if (auto sigma = type->isa<Sigma>()) {
436 if (auto var = sigma->has_var()) {
437 if (is_frozen()) return nullptr; // if frozen, we don't risk rewriting
438 auto t = VarRewriter(var, d).rewrite(sigma->op(*lidx));
439 return unify<Extract>(t, d, index);
440 }
441
442 return unify<Extract>(sigma->op(*lidx), d, index);
443 }
444 }
445
446 const Def* elem_t;
447 if (auto arr = type->isa<Arr>()) {
448 elem_t = arr->reduce(index);
449 } else {
450 auto sigma = type->as<Sigma>();
451 elem_t = nullptr;
452 // «(a_0, ..., a_{n-1})#index; body» is more precise than the join if all ops are Arrs of the same body.
453 if (sigma->isa_imm()) {
454 const Def* body = nullptr;
455 auto extents = DefVec();
456 for (auto op : sigma->ops()) {
457 auto op_arr = op->zonk()->isa<Arr>();
458 if (!op_arr || (body && op_arr->body()->zonk() != body)) {
459 extents.clear();
460 break;
461 }
462 body = op_arr->body()->zonk();
463 extents.emplace_back(op_arr->arity());
464 }
465 if (!extents.empty()) elem_t = this->arr(extract(tuple(extents), index), body);
466 }
467 if (!elem_t) elem_t = join(sigma->ops());
468 }
469
470 if (index->isa<Top>()) {
471 if (auto hole = Hole::isa_unset(d)) {
472 auto elem_hole = mut_hole(elem_t);
473 hole->set(pack(size, elem_hole));
474 return elem_hole;
475 }
476 }
477
478 assert(d);
479 return unify<Extract>(elem_t, d, index);
480}
481
482const Def* World::insert(const Def* d, const Def* index, const Def* val) {
483 d = d->zonk();
484 index = index->zonk();
485 val = val->zonk();
486
487 auto type = d->unfold_type();
488 auto size = Idx::isa(index->unfold_type());
489 auto lidx = Lit::isa(index);
490
491 if (!size) index->blame("index `{}` must be of `Idx` type but is of type `{}`", index, type_of(index)).bail();
492
494 index->blame("index `{}` does not fit within arity `{}`", index, type->arity()).bail();
495
496 if (lidx) {
497 auto elem_type = type->proj(*lidx);
498 auto new_val = Checker::assignable(elem_type, val);
499 if (!new_val) {
500 val->blame("value is not assignable to element type")
501 .n("expected `{}`, got `{}`", elem_type, type_of(val))
502 .n("value: `{}`", val)
503 .bail();
504 }
505 val = new_val;
506 }
507
508 if (auto l = Lit::isa(size); l && *l == 1)
509 return tuple(d, {val}); // d could be mut - that's why the tuple ctor is needed
510
511 // insert((a, b, c, d), 2, x) -> (a, b, x, d)
512 if (auto t = d->isa<Tuple>(); t && lidx) {
513 auto new_ops = DefVec(t->ops().begin(), t->ops().end());
514 new_ops[*lidx] = val;
515 return tuple(type, new_ops);
516 }
517
518 // insert(‹4; x›, 2, y) -> (x, x, y, x)
519 if (auto pack = d->isa<Pack>(); pack && lidx) {
520 if (auto a = Lit::isa(pack->arity()); a && *a < flags().scalarize_threshold) {
521 auto new_ops = DefVec(*a, pack->body());
522 new_ops[*lidx] = val;
523 return tuple(type, new_ops);
524 }
525 }
526
527 // insert(insert(x, index, y), index, val) -> insert(x, index, val)
528 if (auto insert = d->isa<Insert>()) {
529 if (insert->index() == index) d = insert->tuple();
530 }
531
532 return unify<Insert>(d, index, val);
533}
534
535const Def* World::seq(bool is_pack, const Def* arity, const Def* body) {
536 arity = arity->zonk();
537 body = body->zonk();
538
539 auto arity_ty = arity->unfold_type();
540 if (!is_shape(arity_ty)) arity->blame("expected arity but got `{}` of type `{}`", arity, arity_ty).bail();
541
542 if (auto a = Lit::isa(arity)) {
543 if (*a == 0) return unit(is_pack);
544 if (*a == 1) return body;
545 }
546
547 // «(a, b, c); body» -> «a; «(b, c); body»»
548 // e.g. when var, but still has array type
549 if (auto arr_arity = arity_ty->isa<Seq>())
550 if (auto n = Lit::isa(arr_arity->arity())) {
551 auto inner = DefVec(*n - 1, [&](u64 i) { return arity->proj(*n, i + 1); });
552 return seq(is_pack, arity->proj(*n, 0), seq(is_pack, tuple(inner), body));
553 }
554
555 if (is_pack) return unify<Pack>(arr(arity, body->unfold_type()), body);
556 return unify<Arr>(body->unfold_type(), arity, body);
557}
558
559const Def* World::seq(bool is_pack, Defs shape, const Def* body) {
560 if (shape.empty()) return body;
561 return seq(is_pack, shape.rsubspan(1), seq(is_pack, shape.back(), body));
562}
563
564const Lit* World::lit(const Def* type, u64 val) {
565 if (!type) return nullptr;
566 type = type->zonk();
567
568 if (auto size = Idx::isa(type)) {
569 if (size->isa<Top>()) {
570 // unsafe but fine
571 } else if (auto s = Lit::isa(size)) {
572 if (*s != 0 && val >= *s) type->blame("index `{}` does not fit within arity `{}`", val, size).bail();
573 } else if (val != 0) { // 0 of any size is allowed
574 type->blame("cannot create literal `{}` of `Idx {}` as size is unknown", val, size).bail();
575 }
576 }
577
578 return unify<Lit>(type, val);
579}
580
581/*
582 * set
583 */
584
585template<bool Up>
586const Def* World::ext(const Def* type) {
587 type = type->zonk();
588
589 if (auto arr = type->isa<Arr>()) return pack(arr->arity(), ext<Up>(arr->body()));
590 if (auto sigma = type->isa<Sigma>())
591 return tuple(sigma, DefVec(sigma->ops(), [this](const Def* op) { return ext<Up>(op); }));
592 return unify<TExt<Up>>(type);
593}
594
595template<bool Up>
596const Def* World::bound(Defs ops_) {
597 auto ops = DefVec();
598 ops.reserve(ops_.size());
599 for (auto op_ : ops_) {
600 auto op = op_->zonk();
601 if (!op->isa<TExt<!Up>>()) ops.emplace_back(op); // ignore: ext<!Up>
602 }
603
604 auto kind = umax<UMax::Type>(ops);
605
606 // has ext<Up> value?
607 if (std::ranges::any_of(ops, [](const Def* op) { return op->isa<TExt<Up>>(); })) return ext<Up>(kind);
608
609 sort_unique(ops);
610
611 if (ops.empty()) return ext<!Up>(kind);
612 if (ops.size() == 1) return ops[0];
613
614 // TODO simplify mixed terms with joins and meets?
615 return unify<TBound<Up>>(kind, ops);
616}
617
618const Def* World::merge(const Def* type, Defs ops_) {
619 type = type->zonk();
620 auto ops = Def::zonk(ops_);
621
622 if (type->isa<Meet>()) {
623 auto types = DefVec(ops.size(), [&](size_t i) { return ops[i]->unfold_type(); });
624 return unify<Merge>(meet(types), ops);
625 }
626
627 assert(ops.size() == 1);
628 return ops[0];
629}
630
631const Def* World::merge(Defs ops_) {
632 auto ops = Def::zonk(ops_);
633 return merge(umax<UMax::Term>(ops), ops);
634}
635
636const Def* World::inj(const Def* type, const Def* value) {
637 type = type->zonk();
638 value = value->zonk();
639
640 if (type->isa<Join>()) return unify<Inj>(type, value);
641 return value;
642}
643
644const Def* World::split(const Def* type, const Def* value) {
645 type = type->zonk();
646 value = value->zonk();
647
648 return unify<Split>(type, value);
649}
650
651const Def* World::match(Defs ops_) {
652 auto ops = Def::zonk(ops_);
653 if (ops.size() == 1) return ops.front();
654
655 auto scrutinee = ops.front();
656 auto arms = ops.span().subspan(1);
657 auto join = scrutinee->isa_type<Join>();
658
659 if (!join)
660 scrutinee
661 ->blame("scrutinee `{}` of a test expression must be of union type but has type `{}`", scrutinee,
662 type_of(scrutinee))
663 .bail();
664
665 if (arms.size() != join->num_ops())
666 scrutinee->blame("test expression has {} arms but union type has {} cases", arms.size(), join->num_ops())
667 .bail();
668
669 for (auto arm : arms)
670 if (!arm->isa_type<Pi>())
671 arm->blame("arm `{}` of test expression does not have a function type but has type `{}`", arm, type_of(arm))
672 .bail();
673
674 std::ranges::sort(arms, GIDLt<const Def*>(), [](const Def* arm) { return arm->isa_type<Pi>()->dom(); });
675
676 const Def* type = nullptr;
677 for (size_t i = 0, e = arms.size(); i != e; ++i) {
678 auto arm = arms[i];
679 auto pi = arm->isa_type<Pi>();
681 arm->blame("domain type `{}` of test-expression arm does not match union case type `{}`", pi->dom(),
682 join->op(i))
683 .bail();
684 type = type ? this->join({type, pi->codom()}) : pi->codom();
685 }
686
687 // A constructor fixes the active union case. Dispatch before the Match can
688 // escape into later lowering phases, where the payload representation may
689 // already have changed (for example, a tensor may have become a buffer).
690 if (auto inj = scrutinee->isa<Inj>()) {
691 for (size_t i = 0, e = arms.size(); i != e; ++i)
693 return app(arms[i], inj->value());
694 scrutinee->blame("injected value type `{}` is not a case of union type `{}`", type_of(inj->value()), join)
695 .bail();
696 }
697
698 return unify<Match>(type, ops);
699}
700
701const Def* World::uniq(const Def* inhabitant) {
702 inhabitant = inhabitant->zonk();
703 // A singleton type sits one level above its inhabitant, so the top of the hierarchy has none.
704 auto t = inhabitant->unfold_type();
705 if (auto tt = t ? t->unfold_type() : nullptr) return unify<Uniq>(tt, inhabitant);
706 inhabitant->blame("`{}` is too high in the universe hierarchy to inhabit a singleton type", inhabitant).bail();
707}
708
709Sym World::append_suffix(Sym symbol, std::string suffix) {
710 auto name = symbol.str();
711
712 auto pos = name.find(suffix);
713 if (pos != std::string::npos) {
714 auto num = name.substr(pos + suffix.size());
715 if (num.empty()) {
716 name += "_1";
717 } else {
718 num = num.substr(1);
719 num = std::to_string(std::stoi(num) + 1);
720 name = name.substr(0, pos + suffix.size()) + "_" + num;
721 }
722 } else {
723 name += suffix;
724 }
725
726 return sym(std::move(name));
727}
728
729Defs World::reduce(const Var* var, const Def* arg) {
730 if (auto i = move_.substs.find({var, arg}); i != move_.substs.end()) return i->second->defs();
731
732 auto mut = var->binder();
733 auto offset = mut->reduction_offset();
734 auto rw = VarRewriter(var, arg);
735 auto rewrite = [&](size_t i) { return rw.rewrite(mut->op(i + offset)); };
736 return cache_reduct(var, arg, mut->num_ops() - offset, rewrite)->defs();
737}
738
739void World::for_each(bool elide_empty, std::function<void(Def*)> f, bool schedule /* = false */) {
740 fe::BFSWorklist<MutSet> queue;
741 for (auto mut : externals().muts())
742 queue.push(mut);
743
744 auto muts = fe::Vector<Def*>();
745 while (!queue.empty()) {
746 auto mut = queue.pop();
747 if (mut->is_closed() && (!elide_empty || mut->is_set())) muts.emplace_back(mut);
748
749 for (auto op : mut->deps())
750 for (auto local_mut : op->local_muts())
751 queue.push(local_mut);
752 }
753
754 // Schedules the mutables in post-order to ensure that they
755 // are emitted in the correct order of dependencies.
756 if (schedule) {
757 const auto mut_nest = Nest(muts);
758 auto schedule = Scheduler::schedule(mut_nest) | std::views::reverse | std::views::filter([&](Def* mut) {
759 return mut->is_closed() && (!elide_empty || mut->is_set());
760 });
761 for (auto* mut : schedule)
762 f(mut);
763 } else {
764 for (auto* mut : muts)
765 f(mut);
766 }
767}
768
769/*
770 * debugging
771 */
772
773#ifdef MIM_ENABLE_CHECKS
774
775void World::breakpoint(u32 gid) { state_.breakpoints.emplace(gid); }
776void World::watchpoint(u32 gid) { state_.watchpoints.emplace(gid); }
777
778const Def* World::gid2def(u32 gid) {
779 auto i = std::ranges::find_if(move_.sea, [=](auto def) { return def->gid() == gid; });
780 if (i == move_.sea.end()) return nullptr;
781 return *i;
782}
783
785 for (auto mut : externals().muts())
786 assert(mut->is_closed() && mut->is_set());
787 for (auto anx : annexes().defs())
788 assert(anx->is_closed());
789 return *this;
790}
791
792#endif
793
794#ifndef DOXYGEN
795template const Def* World::umax<UMax::Term>(Defs);
796template const Def* World::umax<UMax::Type>(Defs);
797template const Def* World::umax<UMax::Kind>(Defs);
798template const Def* World::umax<UMax::Univ>(Defs);
799template const Def* World::ext<true>(const Def*);
800template const Def* World::ext<false>(const Def*);
801template const Def* World::bound<true>(Defs);
802template const Def* World::bound<false>(Defs);
803template const Def* World::app<true>(const Def*, const Def*);
804template const Def* World::app<false>(const Def*, const Def*);
805template const Def* World::implicit_app<true>(const Def*, const Def*);
806template const Def* World::implicit_app<false>(const Def*, const Def*);
807#endif
808
809// Interning here - once per push - instead of in unify() keeps ~170k redundant Driver::dbg lookups per compile
810// off the hot path: only a few thousand distinct Loc%s occur, yet every emitted Def wants one.
811// Restore rolls both fields back together, so popping a scope never re-interns either.
813 auto& curr = state_.pod.curr_loc;
814 if (loc == curr.loc) return ScopedLoc(curr); // nested emitters push the same Loc; don't re-intern it
815 return ScopedLoc(curr, {loc, loc ? driver().dbg(Dbg(loc)) : DbgKey()});
816}
817
818} // namespace mim
A (possibly paramterized) Array.
Definition tuple.h:110
Definition axm.h:9
static std::tuple< const Axm *, u8, u8 > next(const Def *callee)
Like Axm::get, but advances the counter as one more App is about to be built on top of callee.
Definition axm.cpp:44
NormalizeFn normalizer() const
Definition axm.h:36
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:121
static bool alpha(const Def *d1, const Def *d2)
Definition check.h:93
static const Def * assignable(const Def *type, const Def *value)
Can value be assigned to sth of type?
Definition check.h:100
Base class for all Defs.
Definition def.h:273
bool is_set() const
Definition def.h:370
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:623
const Def * zonk() const
If Holes have been filled, reconstruct the program without them.
Definition check.cpp:21
constexpr auto ops() const noexcept
Definition def.h:348
size_t reduction_offset() const noexcept
First Def::op that needs to be dealt with during reduction; e.g.
Definition def.cpp:578
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:580
const Def * op(size_t i) const noexcept
Definition def.h:351
const Def * unfold_type() const
Yields the type of this Def and builds a new Type (UInc n) if necessary.
Definition def.cpp:451
fe::Error & blame(fe::cite_string< Args... > s, Args &&... args) const
Reports an error that blames this; chain Error::n for Notes and Error::bail to throw.
Definition def.h:310
bool is_external() const noexcept
Definition def.h:553
Loc loc() const
Definition def.h:611
Sym sym() const
Definition def.h:612
const Def * var_type()
If this is a binder, compute the type of its Variable.
Definition def.cpp:232
const Def * arity() const
Number of elements available to Extract / Insert (may be dynamic).
Definition def.cpp:592
constexpr auto reduce(const Def *arg) const
Definition def.h:660
const T * isa_imm() const
Definition def.h:574
bool is_closed() const
Same as !has_free_vars().
Definition def.cpp:353
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
Definition def.h:483
const T * isa_type() const
Is Def::unfold_type a T? Yields nullptr for Univ, which has no type at all.
Definition def.h:337
constexpr size_t num_ops() const noexcept
Definition def.h:352
Some "global" variables needed all over the place.
Definition driver.h:63
Flags & flags()
Definition driver.h:76
fe::Log & log()
Definition driver.h:78
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:161
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:16
static Hole * isa_unset(const Def *def)
Definition check.h:53
static nat_t as_lit(const Def *def)
Definition def.h:995
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:645
Constructs a Join value.
Definition lattice.h:67
Creates a new Tuple / Pack by inserting Insert::value at position Insert::index into Insert::tuple.
Definition tuple.h:186
A function.
Definition lam.h:113
const Def * filter() const
Definition lam.h:125
const Def * body() const
Definition lam.h:126
static std::optional< T > isa(const Def *def)
Definition def.h:937
Nest(Def *root)
Definition nest.cpp:9
A (possibly paramterized) Tuple.
Definition tuple.h:137
A dependent function type.
Definition lam.h:14
const Def * dom() const
Definition lam.h:35
const Def * codom() const
Definition lam.h:36
static Pi * isa_implicit(const Def *d)
Is d an Pi::is_implicit (mutable) Pi?
Definition lam.h:62
const Def * reduce(const Def *arg) const
Definition lam.h:101
static Schedule schedule(const Nest &)
Definition schedule.cpp:121
Base class for Arr and Pack.
Definition tuple.h:75
A dependent tuple type.
Definition tuple.h:23
static const Def * infer(World &, Defs)
Definition check.cpp:423
Extremum. Either Top (Up) or Bottom.
Definition lattice.h:147
Data constructor for a Sigma.
Definition tuple.h:61
static const Def * infer(World &, Defs)
Definition check.cpp:419
const Def * level() const
Definition def.h:903
@ Type
Definition def.h:864
@ Univ
Definition def.h:864
@ Term
Definition def.h:864
@ Kind
Definition def.h:864
VarRewriter(World &world)
Definition rewrite.h:118
A variable introduced by a binder (mutable).
Definition def.h:825
Def * binder() const
The binder of this Var.
Definition def.h:835
Driver & driver()
Definition world.h:220
void attach_alias(flags_t, Sym)
Registers a further Sym for an already attach()ed annex, sharing its flags_t;.
Definition world.cpp:72
const Def * attach(flags_t, Sym, const Def *)
Definition world.cpp:61
void internalize(Def *)
Definition world.cpp:54
void externalize(Def *)
Definition world.cpp:47
const Lit * lit_idx(nat_t size, u64 val)
Constructs a Lit of type Idx of size size.
Definition world.h:557
const Def * insert(const Def *d, const Def *i, const Def *val)
Definition world.cpp:482
const Def * meet(Defs ops)
Definition world.h:599
const Def * uinc(const Def *op, level_t offset=1)
Definition world.cpp:151
const Lit * lit(const Def *type, u64 val)
Definition world.cpp:564
const Def * seq(bool is_pack, const Def *arity, const Def *body)
Definition world.cpp:535
auto & muts()
Definition world.h:695
const Lit * lit_i8()
Definition world.h:551
World(Driver *, Sym name)
Definition world.cpp:113
void watchpoint(u32 gid)
Trigger breakpoint in your debugger when Def::setting a Def with this gid.
Definition world.cpp:776
Driver & driver()
Definition world.h:103
const Type * type(const Def *level)
Definition world.cpp:140
const Lit * lit_tt()
Definition world.h:584
const Def * filter(Lam::Filter filter)
Definition world.h:397
const Def * sigma(Defs ops)
Definition world.cpp:316
const Def * pack(const Def *arity, const Def *body)
Definition world.h:471
const Def * app(const Def *callee, const Def *arg)
Definition world.cpp:237
const Def * match(Defs)
Definition world.cpp:651
const Pi * pi(const Def *dom, const Def *codom, bool implicit=false)
Definition world.h:373
ScopedLoc push(Loc)
Definition world.cpp:812
const Def * unit(bool is_pack)
Definition world.h:490
World & verify()
Verifies that all externals() and annexes() are Def::is_closed(), if MIM_ENABLE_CHECKS.
Definition world.cpp:784
const Idx * type_idx()
Definition world.h:617
const Lit * lit_univ_0()
Definition world.h:534
Sym name() const
Definition world.h:109
const fe::Log & log() const
Log via log().e("...", args) etc.; owned by the Driver.
Definition world.cpp:129
const Lit * lit_univ_1()
Definition world.h:535
const Nat * type_nat()
Definition world.h:616
void for_each(bool elide_empty, std::function< void(Def *)>, bool schedule=false)
Definition world.cpp:739
Hole * mut_hole(const Def *type)
Definition world.h:340
const Lam * lam(const Pi *pi, Lam::Filter f, const Def *body)
Definition world.h:401
const Def * tuple(Defs ops)
Definition world.cpp:326
const Def * gid2def(u32 gid)
Lookup Def by gid.
Definition world.cpp:778
Flags & flags()
Retrieve compile Flags.
Definition world.cpp:130
const Def * implicit_app(const Def *callee, const Def *arg)
Definition world.cpp:230
Annexes & annexes()
Definition world.h:281
const Def * inj(const Def *type, const Def *value)
Definition world.cpp:636
const Type * type()
Definition world.h:329
const Axm * axm(NormalizeFn n, u8 curry, u8 trip, const Def *type, plugin_t p, tag_t t, sub_t s)
Definition world.h:355
fe::Error & error()
Definition world.cpp:127
const Def * extract(const Def *d, const Def *i)
Definition world.cpp:373
bool is_frozen() const
Definition world.h:145
const Def * arr(const Def *arity, const Def *body)
Definition world.h:470
Sym sym(std::string_view)
Definition world.cpp:133
const Lit * lit_ff()
Definition world.h:583
const Def * bound(Defs ops)
Definition world.cpp:596
fe::Restore< CurrLoc > ScopedLoc
Definition world.h:125
const Def * join(Defs ops)
Definition world.h:598
const Def * ext(const Def *type)
Definition world.cpp:586
Sym append_suffix(Sym name, std::string suffix)
Appends a suffix or an increasing number if the suffix already exists.
Definition world.cpp:709
const Lit * lit_idx_1_0()
Definition world.h:548
const Lit * lit_univ(u64 level)
Definition world.h:533
const Def * var(Def *mut)
Definition world.cpp:216
const Tuple * tuple()
the unit value of type []
Definition world.h:506
const Def * uniq(const Def *inhabitant)
Definition world.cpp:701
const Def * raw_app(const Axm *axm, u8 curry, u8 trip, const Def *type, const Def *callee, const Def *arg)
Definition world.cpp:312
const Externals & externals() const
Definition world.h:278
const Def * umax(Defs)
Definition world.cpp:171
const Def * merge(const Def *type, Defs ops)
Definition world.cpp:618
const Sigma * sigma()
The unit type within Type 0.
Definition world.h:458
const Lit * lit_nat(nat_t a)
Definition world.h:540
const State & state() const
Definition world.h:102
Defs reduce(const Var *var, const Def *arg)
Yields the new body of [mut->var() -> arg]mut.
Definition world.cpp:729
void breakpoint(u32 gid)
Trigger breakpoint in your debugger when creating a Def with this gid.
Definition world.cpp:775
const Def * split(const Def *type, const Def *value)
Definition world.cpp:644
Definition ast.h:16
u64 nat_t
Definition types.h:37
u64 flags_t
Definition types.h:39
fe::View< const Def * > Defs
Definition def.h:91
TBound< true > Join
AKA union.
Definition lattice.h:167
u64 level_t
Definition types.h:36
TExt< true > Top
Definition lattice.h:165
fe::Vector< const Def * > DefVec
Definition def.h:93
uint32_t u32
Definition types.h:27
static void flatten_umax(DefVec &ops, const Def *def)
Definition world.cpp:162
uint64_t u64
Definition types.h:27
auto type_of(const Def *def)
Def::unfold_type of def for a diagnostic - Univ is the one Def that has no type at all.
Definition def.h:1136
uint8_t u8
Definition types.h:27
TBound< false > Meet
AKA intersection.
Definition lattice.h:166
static std::string demangle(plugin_t plugin)
Reverts an Axm::mangled plugin back to its name; never longer than Annex::Max_Plugin_Size.
Definition plugin.cpp:33
Compiler switches that must be saved and looked up in later phases of compilation.
Definition flags.h:11