MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
def.cpp
Go to the documentation of this file.
1#include "mim/def.h"
2
3#include <algorithm>
4
5#include <fe/assert.h>
6#include <fe/hash.h>
7#include <fe/worklist.h>
8
9#include "mim/driver.h"
10#include "mim/rule.h"
11#include "mim/world.h"
12
13using namespace std::literals;
14
15#ifndef DOXYGEN // fe::Patricia is not part of the documented input
16template void fe::Patricia<const mim::Var, mim::DefKey>::Set::dump() const;
17template void fe::Patricia<mim::Def, mim::DefKey>::Set::dump() const;
18#endif
19
20namespace mim {
21
22std::ostream& DefKey::stream(std::ostream& os, const Def* d) { return os << d->sym() << ": " << d->gid(); }
23
24/*
25 * constructors
26 */
27
28// The Deps a node contributes to *itself*; Def::dep_ then accumulates the ones of its deps() on top.
29static constexpr unsigned node2dep(Node node, bool mut) {
30 auto dep = mut ? Dep::Mut : Dep::None;
31 // clang-format off
32 switch (node) {
33 case Node::Hole: dep |= Dep::Hole; break;
34 case Node::Proxy: dep |= Dep::Proxy; break;
35 case Node::Var: dep |= Dep::Var | Dep::Mut; break;
36 default: break;
37 }
38 // clang-format on
39 return fe::to_underlying(dep);
40}
41
42Def::Def(World* world, Node node, const Def* type, Defs ops, flags_t flags)
43 : world_(world)
44 , flags_(flags)
45 , node_(node)
46 , mut_(false)
47 , external_(false)
48 , annex_(false)
49 , dirty_(false)
50 , dep_(node2dep(node, false))
51 , num_ops_(ops.size())
52 , type_(type) {
53 if (node == Node::Univ) {
54 gid_ = world->next_gid();
55 hash_ = fe::hash_begin(node_t(Node::Univ));
56 } else {
57 hash_ = fe::hash_begin(u8(node));
58 hash_ = fe::hash_combine(hash_, flags_);
59
60 if (type) {
61 world = &type->world();
62 dep_ |= type->dep_;
63 vars_ = type->local_vars();
64 muts_ = type->local_muts();
65 hash_ = fe::hash_combine(hash_, type->gid());
66 } else {
67 world = &ops[0]->world();
68 }
69
70 auto vars = &world->vars();
71 auto muts = &world->muts();
72 auto ptr = ops_ptr();
73 gid_ = world->next_gid();
74
75 if (node == Node::Proxy) {
76 for (size_t i = 0, e = ops.size(); i != e; ++i) {
77 auto op = ops[i];
78 ptr[i] = op;
79 hash_ = fe::hash_combine(hash_, op->gid());
80 }
81 } else {
82 for (size_t i = 0, e = ops.size(); i != e; ++i) {
83 auto op = ops[i];
84 ptr[i] = op;
85 dep_ |= op->dep_;
86 vars_ = vars->merge(vars_, op->local_vars());
87 muts_ = muts->merge(muts_, op->local_muts());
88 hash_ = fe::hash_combine(hash_, op->gid());
89 }
90 }
91 }
92}
93
94Def::Def(Node n, const Def* type, Defs ops, flags_t flags)
95 : Def(nullptr, n, type, ops, flags) {}
96
97Def::Def(Node node, const Def* type, size_t num_ops, flags_t flags)
98 : flags_(flags)
99 , node_(node)
100 , mut_(true)
101 , external_(false)
102 , annex_(false)
103 , dirty_(false)
104 , dep_(node2dep(node, true))
105 , num_ops_(num_ops)
106 , type_(type) {
107 gid_ = world().next_gid();
108 hash_ = fe::hash(gid());
109 var_ = nullptr;
110 std::fill_n(ops_ptr(), num_ops, nullptr);
111}
112
113Def::Def(Node node, Def* binder)
114 : binder_(binder) // the binder is stored here instead of as an op, so a Var stays out of the operand graph
115 , flags_(0)
116 , node_(node)
117 , mut_(false)
118 , external_(false)
119 , annex_(false)
120 , dirty_(false)
121 , dep_(node2dep(node, false))
122 , num_ops_(0)
123 , type_(nullptr) {
124 gid_ = binder->world().next_gid();
125 vars_ = Vars(as<Var>());
126 hash_ = fe::hash_begin(node_t(Node::Var));
127 hash_ = fe::hash_combine(hash_, binder->gid());
128}
129
130Nat::Nat(World& world)
131 : Def(Node, world.type(), Defs{}, 0) {}
132
133UMax::UMax(World& world, Defs ops)
134 : Def(Node, world.univ(), ops, 0) {}
135
136/*
137 * immutabilize
138 */
139
141 if (!is_set()) return false;
142
143 auto v = has_var();
144 for (auto op : deps()) {
145 if (v && op->has_free_var(v)) return false;
146 for (auto mut : op->local_muts())
147 if (mut == this) return false; // recursion
148 }
149 return true;
150}
151
152/*
153 * reduce
154 */
155
156Defs Def::reduce_(const Def* arg) const {
157 if (auto var = has_var()) return world().reduce(var, arg);
158 auto off = reduction_offset();
159 return {ops().begin() + off, num_ops() - off};
160}
161
162/*
163 * Def - set
164 */
165
166void Def::watch() const {
167#ifdef MIM_ENABLE_CHECKS
168 if (world().watchpoints().contains(gid())) fe::breakpoint();
169#endif
170}
171
172Def* Def::finalize() {
173 if (auto t = check()->zonk(); t != type()) type_ = t;
174 return this;
175}
176
178 watch();
179 invalidate();
180
181 size_t n = ops.size();
182 assert(n == num_ops() && "num ops don't match");
183
184 for (size_t i = 0; i != n; ++i) {
185 auto def = check(i, ops[i]);
186 assert(def);
187 ops_ptr()[i] = def;
188 }
189#ifndef NDEBUG
190 curr_op_ = n;
191#endif
192
193 return finalize();
194}
195
196Def* Def::set(size_t i, const Def* def) {
197 watch();
198 invalidate();
199 def = check(i, def);
200 assert(def && !op(i) && curr_op_++ == i);
201 ops_ptr()[i] = def;
202
203 if (i + 1 == num_ops()) return finalize(); // set last op, so check kind
204 return this;
205}
206
207Def* Def::set_type(const Def* type) {
208 invalidate();
209 type_ = type;
210 return this;
211}
212
214 invalidate();
215#ifndef NDEBUG
216 curr_op_ = 0;
217#endif
218 std::fill_n(ops_ptr(), num_ops(), nullptr);
219 return this;
220}
221
222/*
223 * free_vars
224 */
225
226const Def* Def::var() {
227 if (var_) return var_;
228 return world().var(this);
229}
230
231// clang-format off
232const Def* Def::var_type() {
233 switch (node()) {
234 case Node::Lam: return as<Lam >()->dom();
235 case Node::Pi: return as<Pi >()->dom();
236 case Node::Rule: return as<Rule>()->dom();
237 case Node::Arr:
238 case Node::Pack: return world().type_idx(arity()); // TODO shapes like (2, 3)
239 case Node::Sigma:
240 case Node::Join:
241 case Node::Meet: return this;
242 case Node::Global:
243 case Node::Hole: return nullptr;
244 default: fe::unreachable();
245 }
246}
247// clang-format on
248
250 if (auto mut = isa_mut()) return mut->free_vars();
251
252 auto& vars = world().vars();
253 auto fvs = local_vars();
254 for (auto mut : local_muts())
255 fvs = vars.merge(fvs, mut->free_vars());
256
257 return fvs;
258}
259
260// free_vars() is a union, so any predicate over it distributes over that union: ask f for the local Vars and
261// then for each local mutable's free_vars() - instead of merging them all into one throw-away Set.
262template<class F>
263static bool any_free_vars(const Def* def, F f) {
264 if (auto mut = def->isa_mut()) return f(mut->free_vars());
265
266 if (f(def->local_vars())) return true;
267 for (auto mut : def->local_muts())
268 if (f(mut->free_vars())) return true;
269
270 return false;
271}
272
273bool Def::has_free_var(const Var* var) const {
274 return any_free_vars(this, [var](Vars fvs) { return fvs.contains(var); });
275}
276
277bool Def::has_free_vars() const {
278 return any_free_vars(this, [](Vars fvs) { return !fvs.empty(); });
279}
280
282 return any_free_vars(this, [vars](Vars fvs) { return vars.has_intersection(fvs); });
286 if (mark_ == 0) {
287 // fixed-point iteration to recompute free vars:
288 // (run - 1) identifies the previous iteration; so make sure to offset run by 2 for the first iteration
289 auto& w = world();
290 bool cyclic = false;
291 w.next_run();
292 free_vars<true>(w, cyclic, w.next_run());
293
294 for (bool todo = cyclic; todo;) {
295 todo = false;
296 free_vars<false>(w, todo, w.next_run());
297 }
298 }
299
300 return vars_;
301}
302
303// w is threaded through instead of re-deriving it via world() - which chases the type chain - at every node.
304template<bool init>
305Vars Def::free_vars(World& w, bool& todo, u32 run) {
306 // If init == true : todo flag detects cycle.
307 // If init == false: todo flag keeps track whether sth changed.
308 //
309 // Recursively recompute free vars. If
310 // * mark_ == 0: Invalid - need to recompute.
311 // * mark_ == run - 1: Previous iteration - need to recompute.
312 // * mark_ == run: We are running in cycles within the *current* iteration of our fixed-point loop.
313 // * otherwise: Valid!
314 if (mark_ != 0 && mark_ != run - 1) {
315 if constexpr (init) todo |= mark_ == run;
316 return vars_;
317 }
318
319 mark_ = run;
320
321 auto fvs0 = vars_;
322 auto fvs = fvs0;
323 auto& muts = w.muts();
324 auto& vars = w.vars();
325
326 for (auto op : deps()) {
327 if constexpr (init) fvs = vars.merge(fvs, op->local_vars());
328
329 for (auto mut : op->local_muts()) {
330 if constexpr (init) mut->muts_ = muts.insert(mut->muts_, this); // register "this" as user of local_mut
331 fvs = vars.merge(fvs, mut->free_vars<init>(w, todo, run));
332 }
333 }
334
335 if (auto var = has_var()) fvs = vars.erase(fvs, var); // FV(λx.e) = FV(e) \ {x}
336
337 if constexpr (!init) todo |= fvs0 != fvs;
338
339 return vars_ = fvs;
340}
341
342void Def::invalidate() {
343 if (mark_ != 0) {
344 mark_ = 0;
345 // TODO optimize if vars empty?
346 for (auto mut : users())
347 mut->invalidate();
348 vars_ = Vars();
349 muts_ = Muts();
350 }
351}
352
353bool Def::is_closed() const {
354 bool closed = !has_free_vars();
355 assert((!is_external() || closed) && "an external must not have free Vars");
356 return closed;
357}
358
360 auto fvs = free_vars();
361 if (fvs.empty()) return isa_mut();
362 // Terminates: the binder of a free Var of `this` sits strictly further out than `this`.
363 return fvs.min()->binder()->outermost_binder();
364}
365
366bool Def::nests(Def* mut, MutSet& checked) {
367 auto var = has_var(); // `this` is fixed across the recursion, and so is its Var
368 auto fvs = mut->free_vars();
369 if (fvs.contains(var)) return true;
370 if (auto [_, ins] = checked.emplace(mut); !ins) return false;
371
372 for (auto fv : fvs)
373 if (this->nests(fv->binder(), checked)) return true;
374
375 return false;
376}
377
378bool Def::nests(Def* mut) {
379 if (!this->has_var()) return false;
380 auto checked = MutSet();
381 return this->nests(mut, checked);
382}
383
384bool Def::nests(const Def* def) {
385 if (auto mut = def->isa_mut()) return this->nests(mut);
386 if (!this->has_var()) return false;
387
388 auto checked = MutSet();
389 for (auto fv : def->free_vars())
390 if (this->nests(fv->binder(), checked)) return true;
391
392 return false;
393}
394
395/*
396 * Def - misc
397 */
398
399Driver& Def::driver() const noexcept { return world().driver(); }
400fe::Error& Def::error() const noexcept { return driver().error(); }
401
402Loc Def::err_loc() const {
403 auto& w = world();
404 if (auto loc = w.get_loc()) return loc;
405 if (auto loc = this->loc()) return loc;
406
407 // Nothing was pushed and def itself is anonymous: settle for the closest Loc among its deps.
408 auto queue = fe::BFSWorklist<DefSet>{this};
409
410 // Charged per inspected dep, not per dequeue: a single high-arity Def would otherwise scan - and
411 // enqueue - its whole operand list before the budget got a say.
412 constexpr size_t Budget = 512; // a diagnostic is not worth traversing the whole World for
413 auto budget = Budget;
414
415 while (!queue.empty()) {
416 for (auto dep : queue.pop()->deps()) {
417 if (budget-- == 0) return {};
418 if (!queue.push(dep)) continue;
419 if (auto loc = dep->loc()) return loc;
420 }
421 }
422
423 return {};
424}
425
426Sym Def::sym(const char* s) const { return world().sym(s); }
427Sym Def::sym(std::string_view s) const { return world().sym(s); }
428Sym Def::sym(std::string s) const { return world().sym(std::move(s)); }
429
430Dbg Def::dbg() const { return world().driver().dbg(dbg_); }
431void Def::set_dbg(Dbg d) const { dbg_ = world().driver().dbg(d); }
432
433// Fills in only what is missing (unless @p ow) and interns *once* - the old form went through set(Loc) and
434// set(Sym) separately, each re-reading Def::dbg_ and re-interning through the Driver's hash table.
435void Def::set_dbg_(Dbg d, bool ow) const {
436 if (ow) return set_dbg(d);
437
438 auto mine = dbg(), merged = mine;
439 if (!merged.loc()) merged.set(d.loc());
440 if (!merged.sym()) merged.set(d.sym());
441 if (!(merged == mine)) set_dbg(merged);
442}
443
444void Def::set_dbg_key_(DbgKey key, bool ow) const {
445 // Nothing of ours to preserve, so adopting the index is exactly what set<Ow>(that Dbg) would compute -
446 // but without materialising a Dbg or touching Driver::dbg2idx_ at all.
447 if (ow || !dbg_ || dbg_ == key) return void(dbg_ = key);
448 set_dbg_(world().driver().dbg(key), false); // rare: we carry a partial Dbg, so merge field-wise
449}
450
451const Def* Def::unfold_type() const {
452 if (auto t = type()) return t;
453 auto& w = world();
454 if (auto t = isa<Type>()) return w.type(w.uinc(t->level()));
455 assert(isa<Univ>());
456 return nullptr;
457}
458
459std::string_view Def::node_name() const {
460 static constexpr std::string_view Names[Num_Nodes] = {
461#define CODE(node, _) #node,
463#undef CODE
464 };
465 return Names[node_t(node())];
466}
467
468Defs Def::deps() const noexcept {
469 // deps() hands out `[type_, op0, op1, ...]` as one contiguous array by stepping back from ops_ptr()
470 // (which is `this + 1`). That only holds while type_ occupies the *last* 8 bytes of Def, so moving it
471 // - or appending any member after it - would silently corrupt every deps() walk.
472 assert((const void*)(ops_ptr() - 1) == (const void*)&type_
473 && "Def::type_ must stay Def's last member: Def::deps() and Def::ops_ptr() depend on it");
474
475 // Univ, Type, and Var are the only nodes built without a type - and none of them has deps.
476 if (!type_) {
477 assert(isa<Univ>() || isa<Type>() || isa<Var>());
478 return Defs();
479 }
480
481 // Not is_set(): its assertion scans *all* ops and deps() is an inner loop.
482 bool set = num_ops_ == 0 || ops_ptr()[num_ops_ - 1];
483 return Defs(ops_ptr() - 1, (set ? num_ops_ : 0) + 1);
484}
485
486bool Def::is_term() const {
487 if (auto t = type())
488 if (auto u = t->type())
489 if (auto type = u->isa<Type>()) return Lit::isa(type->level()) == nat_t(0);
490 return false;
491}
492
493#ifndef NDEBUG
494const Def* Def::debug_prefix(std::string prefix) const { return set_dbg(dbg().set(sym(prefix + sym().str()))), this; }
495const Def* Def::debug_suffix(std::string suffix) const { return set_dbg(dbg().set(sym(sym().str() + suffix))), this; }
496#endif
497
498/*
499 * cmp
500 */
501
502Def::Cmp Def::cmp(const Def* a, const Def* b) {
503 if (a == b) return Cmp::E;
504
505 if (a->is_mutable() != b->is_mutable()) return a->is_mutable() ? Cmp::G : Cmp::L;
506
507 // clang-format off
508 if (a->node() != b->node() ) return a->node() < b->node() ? Cmp::L : Cmp::G;
509 if (a->num_ops() != b->num_ops()) return a->num_ops() < b->num_ops() ? Cmp::L : Cmp::G;
510 if (a->flags() != b->flags() ) return a->flags() < b->flags() ? Cmp::L : Cmp::G;
511 // clang-format on
512
513 if (a->is_mutable()) return Cmp::U; // two distinct mutables of the same shape are incomparable
514
515 if (auto va = a->isa<Var>()) {
516 auto vb = b->as<Var>();
517 auto ma = va->binder();
518 auto mb = vb->binder();
519 if (ma->is_set() && ma->free_vars().contains(vb)) return Cmp::L;
520 if (mb->is_set() && mb->free_vars().contains(va)) return Cmp::G;
521 return Cmp::U;
522 }
523
524 // heuristic: iterate backwards as index (often a Lit) comes last and will faster find a solution
525 for (size_t i = a->num_ops(); i-- != 0;)
526 if (auto res = cmp(a->op(i), b->op(i)); res == Cmp::L || res == Cmp::G) return res;
527
528 return cmp(a->type(), b->type());
529}
530
531template<Def::Cmp c>
532bool Def::cmp_(const Def* a, const Def* b) {
533 auto res = cmp(a, b);
534 if (res == Cmp::U) {
535 a->world().log().w("commute check resorts to an unstable gid-based compare");
536 return c == Cmp::L ? a->gid() < b->gid() : a->gid() > b->gid();
537 }
538 return res == c;
539}
540
541// clang-format off
542bool Def::less (const Def* a, const Def* b) { return cmp_<Cmp::L>(a, b); }
543bool Def::greater(const Def* a, const Def* b) { return cmp_<Cmp::G>(a, b); }
544// clang-format on
545
546// clang-format off
547
548/*
549 * dispatch
550 *
551 * All of these used to be `virtual`. Def has no vtable - a vptr would cost 8 bytes on *every* node in the
552 * World - so each is one function switching on Def::node() with the former override inlined right here.
553 */
554
555const Def* Def::immutabilize() {
556 auto& w = world();
557 switch (node()) {
558 case Node::Pi: return is_immutabilizable() ? w.pi(as<Pi>()->dom(), as<Pi>()->codom()) : nullptr;
559 case Node::Sigma: return is_immutabilizable() ? w.sigma(ops()) : nullptr;
560 case Node::Rule: return nullptr; // TODO should we ever immutabilize Rules?
561 case Node::Arr:
562 case Node::Pack: {
563 auto seq = as<Seq>();
564 auto arr = node() == Node::Arr;
565 if (is_immutabilizable())
566 return arr ? w.arr(seq->arity(), seq->body()) : w.pack(seq->arity(), seq->body());
567 // below the threshold an unrollable Seq becomes an explicit Sigma/Tuple
568 if (auto n = Lit::isa(seq->arity()); n && *n < w.flags().scalarize_threshold) {
569 auto elems = DefVec(*n, [&](size_t i) { return seq->reduce(w.lit_idx(*n, i)); });
570 return arr ? w.sigma(elems) : w.tuple(elems);
571 }
572 return nullptr;
573 }
574 default: return nullptr;
575 }
576}
577
578size_t Def::reduction_offset() const noexcept {
579 switch (node()) {
580 case Node::Join:
581 case Node::Lam:
582 case Node::Meet:
583 case Node::Pack:
584 case Node::Sigma: return 0;
585 case Node::Arr:
586 case Node::Pi:
587 case Node::Rule: return 1;
588 default: return size_t(-1);
589 }
590}
591
592const Def* Def::arity() const {
593 switch (node()) {
594 case Node::Arr: return op(0);
595 case Node::Sigma: return num_ops() != 1 || isa_mut() ? world().lit_nat(num_ops()) : op(0)->arity();
596 case Node::Pack:
597 if (auto arr = type()->isa<Arr>()) return arr->arity();
598 return type() == world().sigma() ? world().lit_nat_0() : world().lit_nat_1();
599 default:
600 if (auto t = type(); t && !t->isa<Type>()) return t->arity();
601 return world().lit_nat_1();
602 }
603}
604
605// clang-format on
606
607void Def::externalize() { return world().externals().externalize(this); }
608void Def::internalize() { return world().externals().internalize(this); }
609
611 assert(this->sym() == to->sym());
612 internalize();
613 to->externalize();
614}
615
616std::string Def::unique_name() const { return sym().str() + "_"s + std::to_string(gid()); }
617
619 if (auto a = Lit::isa(arity()); a && *a < world().flags().scalarize_threshold) return *a;
620 return 1;
621}
622
623const Def* Def::proj(nat_t a, nat_t i) const {
624 if (a == 1) {
625 assert(i == 0 && "only inhabitant of Idx 2 is 0_1");
626 auto t = type();
627 if (!t) return this;
628 if (!isa_mut<Sigma>() && !t->isa_mut<Sigma>()) return this;
629 }
630
631 if (auto seq = isa<Seq>()) {
632 if (seq->has_var()) return seq->reduce(world().lit_idx(a, i));
633 return seq->body();
634 }
635
636 if (isa<Prod>()) return op(i);
637
638 return world().extract(this, a, i); // only compute world() on the path that needs it
639}
640
641/*
642 * Idx
643 */
644
645const Def* Idx::isa(const Def* def) {
646 if (auto app = def ? def->isa<App>() : nullptr) {
647 if (app->callee()->isa<Idx>()) return app->arg();
648 }
649
650 return nullptr;
651}
652
653std::optional<nat_t> Idx::isa_lit(const Def* def) {
654 if (auto size = Idx::isa(def))
655 if (auto l = Lit::isa(size)) return l;
656 return {};
657}
658
659std::optional<nat_t> Idx::size2bitwidth(const Def* size) {
660 if (size->isa<Top>()) return 64;
661 if (auto s = Lit::isa(size)) return size2bitwidth(*s);
662 return {};
663}
664
665/*
666 * Global
667 */
668
669const App* Global::type() const { return Def::type()->as<App>(); }
670const Def* Global::alloced_type() const { return type()->arg(0); }
671
672} // namespace mim
const Def * arg() const
Definition lam.h:284
Base class for all Defs.
Definition def.h:273
bool is_set() const
Definition def.h:370
Loc err_loc() const
Returns a blame Loc from World::get_loc, this Def, or its nearest located dependency,...
Definition def.cpp:402
void set_dbg(Dbg) const
Interns dbg via Driver::dbg and stores the key in Def::dbg_.
Definition def.cpp:431
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
constexpr Node node() const noexcept
Definition def.h:297
Def * set(size_t i, const Def *)
Successively set from left to right.
Definition def.cpp:196
Defs deps() const noexcept
Definition def.cpp:468
nat_t num_tprojs() const
As above but yields 1, if Flags::scalarize_threshold is exceeded.
Definition def.cpp:618
const Def * zonk() const
If Holes have been filled, reconstruct the program without them.
Definition check.cpp:21
World & world() const noexcept
Definition def.h:1097
Def * set_type(const Def *)
Update type.
Definition def.cpp:207
std::string_view node_name() const
Definition def.cpp:459
fe::Error & error() const noexcept
Definition def.cpp:400
constexpr auto ops() const noexcept
Definition def.h:348
Vars local_vars() const
Vars reachable by following immutable deps().
Definition def.h:514
size_t reduction_offset() const noexcept
First Def::op that needs to be dealt with during reduction; e.g.
Definition def.cpp:578
constexpr flags_t flags() const noexcept
Definition def.h:293
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:580
auto vars() noexcept
Definition def.h:479
void externalize()
Definition def.cpp:607
bool is_term() const
Is this Def a term, i.e. is its type() a Type?
Definition def.cpp:486
const Def * debug_prefix(std::string) const
Definition def.cpp:494
const Def * op(size_t i) const noexcept
Definition def.h:351
bool is_immutabilizable()
Definition def.cpp:140
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:479
void transfer_external(Def *to)
Definition def.cpp:610
const Def * unfold_type() const
Yields the type of this Def and builds a new Type (UInc n) if necessary.
Definition def.cpp:451
bool has_free_vars() const
Same as !free_vars().empty().
Definition def.cpp:277
friend class World
Definition def.h:796
bool has_free_var(const Var *) const
Same as free_vars().contains(var).
Definition def.cpp:273
void set_dbg_(Dbg, bool ow) const
Backs Def::set(Dbg).
Definition def.cpp:435
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
Definition def.h:507
const Def * debug_suffix(std::string) const
Definition def.cpp:495
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1111
Def * outermost_binder() const
Transitively walks up free_vars() till the outermoust binder has been found.
Definition def.cpp:359
bool nests(Def *mut)
Does this nest mut?
Definition def.cpp:378
const Def * dep(size_t i) const noexcept
Definition def.h:393
bool is_mutable() const noexcept
Definition def.h:571
bool is_external() const noexcept
Definition def.h:553
auto vars(F f) noexcept
Definition def.h:479
void internalize()
Definition def.cpp:608
static bool less(const Def *a, const Def *b)
Definition def.cpp:542
static bool greater(const Def *a, const Def *b)
Definition def.cpp:543
Loc loc() const
Definition def.h:611
const Def * var()
Not necessarily a Var: E.g., if the return type is [], this will yield ().
Definition def.cpp:226
static Cmp cmp(const Def *a, const Def *b)
Definition def.cpp:502
Sym sym() const
Definition def.h:612
Driver & driver() const noexcept
Definition def.cpp:399
flags_t flags_
Definition def.h:770
const Def * immutabilize()
Definition def.cpp:555
const Def * var_type()
If this is a binder, compute the type of its Variable.
Definition def.cpp:232
constexpr u32 gid() const noexcept
Global id - unique number for this Def.
Definition def.h:294
const Def * arity() const
Number of elements available to Extract / Insert (may be dynamic).
Definition def.cpp:592
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
Definition def.cpp:213
std::string unique_name() const
name + "_" + Def::gid
Definition def.cpp:616
void set_dbg_key_(DbgKey, bool ow) const
Backs Def::set(DbgKey).
Definition def.cpp:444
Muts users()
Set of mutables where this mutable is locally referenced.
Definition def.h:520
bool is_closed() const
Same as !has_free_vars().
Definition def.cpp:353
Vars free_vars() const
Global set of free Vars: extends local_vars() by transitively following mutables as well.
Definition def.cpp:249
Dbg dbg() const
Looks up Def::dbg_ in Driver::dbg.
Definition def.cpp:430
const Def * check(size_t i, const Def *def)
Checks whether the ith operand can be set to def.
Definition check.cpp:438
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
Definition def.h:483
bool has_free_vars_in(Vars) const
Same as vars.has_intersection(free_vars()).
Definition def.cpp:281
constexpr size_t num_ops() const noexcept
Definition def.h:352
@ E
Equal.
Definition def.h:715
@ U
Unknown.
Definition def.h:716
@ L
Less.
Definition def.h:713
@ G
Greater.
Definition def.h:714
Some "global" variables needed all over the place.
Definition driver.h:63
const Def * alloced_type() const
Definition def.cpp:670
const App * type() const
Definition def.cpp:669
static constexpr nat_t size2bitwidth(nat_t n)
Definition def.h:1006
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:645
static std::optional< nat_t > isa_lit(const Def *def)
Definition def.cpp:653
static std::optional< T > isa(const Def *def)
Definition def.h:937
A dependent tuple type.
Definition tuple.h:23
A variable introduced by a binder (mutable).
Definition def.h:825
Def * binder() const
The binder of this Var.
Definition def.h:835
void internalize(Def *)
Definition world.cpp:54
void externalize(Def *)
Definition world.cpp:47
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:40
auto & muts()
Definition world.h:695
Driver & driver()
Definition world.h:103
const Def * sigma(Defs ops)
Definition world.cpp:316
const Idx * type_idx()
Definition world.h:617
Flags & flags()
Retrieve compile Flags.
Definition world.cpp:130
u32 next_gid()
Definition world.h:115
const Def * extract(const Def *d, const Def *i)
Definition world.cpp:373
const Lit * lit_nat_0()
Definition world.h:545
Sym sym(std::string_view)
Definition world.cpp:133
const Def * var(Def *mut)
Definition world.cpp:216
const Externals & externals() const
Definition world.h:278
const Lit * lit_nat(nat_t a)
Definition world.h:540
auto & vars()
Definition world.h:694
Defs reduce(const Var *var, const Def *arg)
Yields the new body of [mut->var() -> arg]mut.
Definition world.cpp:729
const Lit * lit_nat_1()
Definition world.h:546
#define MIM_NODE(X)
Definition def.h:26
Definition ast.h:16
u64 nat_t
Definition types.h:37
@ Var
Depends on a Var.
Definition def.h:136
@ Hole
Depends on a Hole.
Definition def.h:137
@ None
Depends on nothing of interest.
Definition def.h:134
@ Mut
Depends on a mutable.
Definition def.h:135
@ Proxy
Depends on a Proxy.
Definition def.h:138
u64 flags_t
Definition types.h:39
static bool any_free_vars(const Def *def, F f)
Definition def.cpp:263
fe::View< const Def * > Defs
Definition def.h:91
u8 node_t
Definition types.h:38
TExt< true > Top
Definition lattice.h:165
GIDSet< Def * > MutSet
Definition def.h:101
fe::Vector< const Def * > DefVec
Definition def.h:93
fe::Patricia< const Var, DefKey >::Set Vars
Definition def.h:112
static constexpr unsigned node2dep(Node node, bool mut)
Definition def.cpp:29
uint32_t u32
Definition types.h:27
fe::Patricia< Def, DefKey >::Set Muts
Definition def.h:103
uint8_t u8
Definition types.h:27
Node
Definition def.h:120
@ Pi
Definition def.h:122
@ Univ
Definition def.h:122
@ Lam
Definition def.h:122
@ Arr
Definition def.h:122
@ Pack
Definition def.h:122
@ Meet
Definition def.h:122
@ Global
Definition def.h:122
@ Var
Definition def.h:122
@ Hole
Definition def.h:122
@ Sigma
Definition def.h:122
@ Join
Definition def.h:122
@ Rule
Definition def.h:122
@ Proxy
Definition def.h:122
static std::ostream & stream(std::ostream &, const Def *)
Definition def.cpp:22
uint64_t scalarize_threshold
Definition flags.h:20
#define CODE(name,...)
Definition tok.h:51