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