MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
def.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <format>
5#include <limits>
6#include <optional>
7#include <span>
8#include <type_traits>
9#include <utility>
10
11#include <fe/algo.h>
12#include <fe/assert.h>
13#include <fe/cast.h>
14#include <fe/container.h>
15#include <fe/enum.h>
16#include <fe/patricia.h>
17#include <fe/vector.h>
18
19#include "mim/config.h"
20
21#include "mim/util/dbg.h"
22#include "mim/util/gid.h"
23#include "mim/util/types.h"
24
25// clang-format off
26#define MIM_NODE(X) \
27 X(Lit, Judge::Intro) /* keep this first - causes Lit to appear left in Def::less/Def::greater*/ \
28 X(Axm, Judge::Intro) \
29 X(Var, Judge::Intro) \
30 X(Global, Judge::Intro) \
31 X(Proxy, Judge::Intro) \
32 X(Hole, Judge::Hole ) \
33 X(Type, Judge::Meta ) X(Univ, Judge::Meta ) X(UMax, Judge::Meta) X(UInc, (Judge::Meta )) \
34 X(Pi, Judge::Form ) X(Lam, Judge::Intro) X(App, Judge::Elim) \
35 X(Sigma, Judge::Form ) X(Tuple, Judge::Intro) X(Extract, Judge::Elim) X(Insert, (Judge::Intro | Judge::Elim)) \
36 X(Arr, Judge::Form ) X(Pack, Judge::Intro) \
37 X(Join, Judge::Form ) X(Inj, Judge::Intro) X(Match, Judge::Elim) X(Top, (Judge::Intro )) \
38 X(Meet, Judge::Form ) X(Merge, Judge::Intro) X(Split, Judge::Elim) X(Bot, (Judge::Intro )) \
39 X(Reform, Judge::Form ) X(Rule, Judge::Intro) \
40 X(Uniq, Judge::Form ) \
41 X(Nat, Judge::Form ) \
42 X(Idx, Judge::Intro)
43
44#define MIM_IMM_NODE(X) \
45 X(Lit) \
46 X(Axm) \
47 X(Var) \
48 X(Proxy) \
49 X(Type) X(Univ) X(UMax) X(UInc) \
50 X(Pi) X(Lam) X(App) \
51 X(Sigma) X(Tuple) X(Extract) X(Insert) \
52 X(Arr) X(Pack) \
53 X(Join) X(Inj) X(Match) X(Top) \
54 X(Meet) X(Merge) X(Split) X(Bot) \
55 X(Reform) X(Rule) \
56 X(Uniq) \
57 X(Nat) \
58 X(Idx)
59
60#define MIM_MUT_NODE(X) \
61 X(Global) \
62 X(Hole) \
63 X(Pi) X(Lam) \
64 X(Sigma) \
65 X(Arr) X(Pack) \
66 X(Rule)
67// clang-format on
68
69namespace mim {
70
71class App;
72class Axm;
73class Var;
74class Def;
75class Driver;
76class World;
77
78/// Grants fe::Patricia access to Def::gid_.
79struct DefKey {
80 static u32 key(const Def*) noexcept;
81 static std::ostream& stream(std::ostream&, const Def*);
82};
83
84/// @name Def
85/// GIDSet / GIDMap keyed by Def::gid of `const Def*`.
86///@{
87template<class To>
91using Defs = fe::View<const Def*>;
92
93using DefVec = fe::Vector<const Def*>;
94///@}
95
96/// @name Def (Mutable)
97/// GIDSet / GIDMap keyed by Def::gid of `Def*`.
98///@{
99template<class To>
103using Muts = fe::Patricia<Def, DefKey>::Set;
104///@}
105
106/// @name Var
107/// GIDSet / GIDMap keyed by Var::gid of `const Var*`.
108///@{
109template<class To>
112using Vars = fe::Patricia<const Var, DefKey>::Set;
113///@}
114
115using NormalizeFn = const Def* (*)(const Def*, const Def*, const Def*);
116
117/// @name Enums that classify certain aspects of Def%s.
118///@{
119
120enum class Node : node_t {
121#define CODE(node, _) node,
123#undef CODE
124};
125
126#define CODE(node, _) +size_t(1)
127static constexpr size_t Num_Nodes = size_t(0) MIM_NODE(CODE);
128#undef CODE
129
130/// Tracks whether a Def transitively depends - through its Def::deps() but only up to (and excluding) the next
131/// *mutable* - on certain kinds of Def%s.
132/// @see Def::has_dep
133enum class Dep : unsigned {
134 None = 0, ///< Depends on nothing of interest.
135 Mut = 1 << 0, ///< Depends on a *mutable*.
136 Var = 1 << 1, ///< Depends on a Var.
137 Hole = 1 << 2, ///< Depends on a Hole.
138 Proxy = 1 << 3, ///< Depends on a Proxy.
139};
140
141/// [Judgement](https://ncatlab.org/nlab/show/judgment).
142enum class Judge : u32 {
143 // clang-format off
144 Form = 1 << 0, ///< [Type Formation](https://ncatlab.org/nlab/show/type+formation) like `T -> T`.
145 Intro = 1 << 1, ///< [Term Introduction](https://ncatlab.org/nlab/show/natural+deduction) like `λ(x: Nat): Nat = x`.
146 Elim = 1 << 2, ///< [Term Elimination](https://ncatlab.org/nlab/show/term+elimination) like `f a`.
147 Meta = 1 << 3, ///< Meta rules for Univ%erse and Type levels.
148 Hole = 1 << 4, ///< Special rule for Hole.
149 // clang-format on
150};
151
152/// Classifies whether a [`Node`](@ref mim::Node) may occur as a *mutable*, an *immutable*, or both.
153/// @see @ref mut
154enum class Mut {
155 // clang-format off
156 Mut = 1 << 0, ///< Node may be mutable.
157 Imm = 1 << 1, ///< Node may be immutable.
158 // clang-format on
159};
160///@}
161
162} // namespace mim
163
164#ifndef DOXYGEN
165// clang-format off
166template<> struct fe::is_bit_enum<mim::Dep> : std::true_type {};
167template<> struct fe::is_bit_enum<mim::Judge> : std::true_type {};
168template<> struct fe::is_bit_enum<mim::Mut> : std::true_type {};
169// clang-format on
170#endif
171
172namespace mim {
173
174/// Use as mixin to wrap all kind of Def::proj and Def::projs variants.
175#define MIM_PROJ(NAME, CONST) \
176 nat_t num_##NAME##s() CONST noexcept { return ((const Def*)NAME())->num_projs(); } \
177 nat_t num_t##NAME##s() CONST noexcept { return ((const Def*)NAME())->num_tprojs(); } \
178 const Def* NAME(nat_t a, nat_t i) CONST noexcept { return ((const Def*)NAME())->proj(a, i); } \
179 const Def* NAME(nat_t i) CONST noexcept { return ((const Def*)NAME())->proj(i); } \
180 const Def* t##NAME(nat_t i) CONST noexcept { return ((const Def*)NAME())->tproj(i); } \
181 template<nat_t A = std::dynamic_extent, class F> \
182 auto NAME##s(F f) CONST noexcept { \
183 return ((const Def*)NAME())->projs<A, F>(f); \
184 } \
185 template<class F> \
186 auto t##NAME##s(F f) CONST noexcept { \
187 return ((const Def*)NAME())->tprojs<F>(f); \
188 } \
189 template<nat_t A = std::dynamic_extent> \
190 auto NAME##s() CONST noexcept { \
191 return ((const Def*)NAME())->projs<A>(); \
192 } \
193 auto t##NAME##s() CONST noexcept { return ((const Def*)NAME())->tprojs(); } \
194 template<class F> \
195 auto NAME##s(nat_t a, F f) CONST noexcept { \
196 return ((const Def*)NAME())->projs<F>(a, f); \
197 } \
198 auto NAME##s(nat_t a) CONST noexcept { return ((const Def*)NAME())->projs(a); }
199
200/// CRTP-based mixin to declare setters for Def::loc \& Def::name using a *covariant* return type.
201/// Forwards every argument list the Def::set%ters accept and hands back a @p P instead of a Def.
202/// @note Setters::Fwd keeps this variadic out of the overload set of the `set` members subclasses declare
203/// themselves (Global::set, Pi::set, ...) - none of those is a template on @p Ow, so they keep winning.
204template<class P, class D = Def>
205class // D is only needed to make the resolution `D::template set` lazy
206#ifdef _MSC_VER
207 __declspec(empty_bases)
208#endif
209 Setters {
210private:
211 P* super() { return static_cast<P*>(this); }
212 const P* super() const { return static_cast<const P*>(this); }
213
214 /// Is `D::set<Ow>(Args...)` a thing?
215 template<bool Ow, class... Args>
216 static constexpr bool Fwd = requires(D* d, Args&&... args) { d->template set<Ow>(std::forward<Args>(args)...); };
217
218public:
219 // clang-format off
220 template<bool Ow = false, class... Args> requires Fwd<Ow, Args...>
221 const P* set(Args&&... args) const { super()->D::template set<Ow>(std::forward<Args>(args)...); return super(); }
222 template<bool Ow = false, class... Args> requires Fwd<Ow, Args...>
223 P* set(Args&&... args) { super()->D::template set<Ow>(std::forward<Args>(args)...); return super(); }
224 // clang-format on
225};
226
227/// Options for Def::dot and World::dot.
228/// @note Def::dot and World::dot honor DotConfig::max; World::dot also honors DotConfig::all_annexes.
229struct DotConfig {
230 int max = std::numeric_limits<int>::max(); ///< Maximum recursion depth.
231 bool all_annexes = false; ///< Include all annexes - even if unused (World::dot only).
232 bool follow_types = false; ///< Follow Def::type() dependencies.
233 bool inline_consts = false; ///< Wire up literals, axioms, etc. with normal edges instead of detaching them.
234 bool default_filter = false; ///< Show Lam::filter() even if it has its default value.
235 bool show_hidden = false; ///< Render otherwise-transparent detached edges (Var→binder back-edges,
236 ///< shared literals/axioms, type edges) with a visible color.
237};
238
239/// Base class for all Def%s.
240///
241/// These are the most important subclasses:
242/// | Type Formation | Term Introduction | Term Elimination |
243/// | ----------------- | ----------------- | ----------------- |
244/// | Pi | Lam | App |
245/// | Sigma / Arr | Tuple / Pack | Extract |
246/// | | Insert | Insert |
247/// | Uniq | | |
248/// | Join | Inj | Match |
249/// | Meet | Merge | Split |
250/// | Reform | Rule | |
251/// | Nat | Lit | |
252/// | Idx | Lit | |
253/// In addition there is:
254/// * Var: A variable. Currently the following Def%s may be binders:
255/// * Pi, Lam, Sigma, Arr, Pack
256/// * Axm: To introduce new entities.
257/// * Proxy: Used for intermediate values during optimizations.
258/// * Hole: A metavariable filled in by the type inference (always mutable as holes are filled in later).
259/// * Type, Univ, UMax, UInc: To keep track of type levels.
260///
261/// The data layout (see World::alloc and Def::deps) looks like this:
262/// ```
263/// Def| type | op(0) ... op(num_ops-1) |
264/// |-----------ops-----------|
265/// deps
266/// |--------------------------------| if type() != nullptr && is_set()
267/// |-------------------------| if type() == nullptr && is_set()
268/// |------| if type() != nullptr && !is_set()
269/// || if type() == nullptr && !is_set()
270/// ```
271/// @attention This means that any subclass of Def **must not** introduce additional members.
272/// @see @ref mut
273class Def : public fe::RuntimeCast<Def> {
274private:
275 Def& operator=(const Def&) = delete;
276 Def(const Def&) = delete;
277 Def(Def&&) = delete;
278
279protected:
280 /// @name C'tors and D'tors
281 ///@{
282 Def(World*, Node, const Def* type, Defs ops, flags_t flags); ///< Constructor for an *immutable* Def.
283 Def(Node, const Def* type, Defs ops, flags_t flags); ///< As above but World retrieved from @p type.
284 Def(Node, const Def* type, size_t num_ops, flags_t flags); ///< Constructor for a *mutable* Def.
285 Def(Node, Def* binder); ///< Constructor for a Var; stores its @p binder.
286 ///@}
287
288public:
289 /// @name Getters
290 ///@{
291 World& world() const noexcept;
292 Driver& driver() const noexcept;
293 constexpr flags_t flags() const noexcept { return flags_; }
294 constexpr u32 gid() const noexcept { return gid_; } ///< Global id - *unique* number for this Def.
295 constexpr u32 mark() const noexcept { return mark_; } ///< Used internally by free_vars().
296 constexpr size_t hash() const noexcept { return hash_; }
297 constexpr Node node() const noexcept { return node_; }
298 std::string_view node_name() const;
299 ///@}
300
301 /// @name Diagnostics
302 ///@{
303 fe::Error& error() const noexcept;
304
305 /// Returns a blame Loc from World::get_loc, this Def, or its nearest located dependency, in that order.
306 Loc err_loc() const;
307
308 /// Reports an error that blames *this*; chain Error::n for Note%s and Error::bail to throw.
309 template<class... Args>
310 fe::Error& blame(fe::cite_string<Args...> s, Args&&... args) const {
311 return error().e(err_loc(), s, std::forward<Args>(args)...);
312 }
313 ///@}
314
315 /// @name Judgement
316 /// What kind of Judge%ment represents this Def?
317 ///@{
318 Judge judge() const noexcept;
319 // clang-format off
320 bool is_form() const noexcept { return fe::has_flag(judge(), Judge::Form); }
321 bool is_intro() const noexcept { return fe::has_flag(judge(), Judge::Intro); }
322 bool is_elim() const noexcept { return fe::has_flag(judge(), Judge::Elim); }
323 bool is_meta() const noexcept { return fe::has_flag(judge(), Judge::Meta); }
324 // clang-format on
325 ///@}
326
327 /// @name type
328 ///@{
329
330 /// Yields the "raw" type of this Def (maybe `nullptr`).
331 /// @see Def::unfold_type.
332 const Def* type() const noexcept;
333 /// Yields the type of this Def and builds a new `Type (UInc n)` if necessary.
334 const Def* unfold_type() const;
335 /// Is Def::unfold_type a @p T? Yields `nullptr` for Univ, which has no type at all.
336 template<class T>
337 const T* isa_type() const {
338 auto t = unfold_type();
339 return t ? t->template isa<T>() : nullptr;
340 }
341 bool is_term() const; ///< Is this Def a *term*, i.e. is its type() a Type?
342 const Def* arity() const; ///< Number of elements available to Extract / Insert (may be dynamic).
343 ///@}
344
345 /// @name ops
346 ///@{
347 template<size_t N = std::dynamic_extent>
348 constexpr auto ops() const noexcept {
349 return fe::View<const Def*, N>(ops_ptr(), num_ops_);
350 }
351 const Def* op(size_t i) const noexcept { return ops()[i]; }
352 constexpr size_t num_ops() const noexcept { return num_ops_; }
353 ///@}
354
355 /// @name Setting Ops (Mutables Only)
356 /// @anchor set_ops
357 /// You can set and change the Def::ops of a mutable after construction.
358 /// However, you have to obey the following rules:
359 /// If Def::is_set() is ...
360 /// * `false`, [set](@ref Def::set) the [operands](@ref Def::ops) from left to right.
361 /// * `true`, Def::unset() the operands first and then start over:
362 /// ```
363 /// mut->unset()->set({a, b, c});
364 /// ```
365 ///
366 /// MimIR assumes that a mutable is *final*, when its last operand is set.
367 /// Then, Def::check() will be invoked.
368 ///@{
369 /// Yields `true` if empty or the last op is set.
370 bool is_set() const {
371 if (num_ops() == 0) return true;
372 bool result = ops().back();
373 assert((!result || std::ranges::all_of(ops().rsubspan(1), [](auto op) { return op; }))
374 && "the last operand is set but others in front of it aren't");
375 return result;
376 }
377 Def* set(size_t i, const Def*); ///< Successively set from left to right.
378 Def* set(Defs ops); ///< Set @p ops all at once (no Def::unset necessary beforehand).
379 Def* unset(); ///< Unsets all Def::ops; works even, if not set at all or only partially set.
380
381 /// Update type.
382 /// @warning Only make type-preserving updates such as removing Hole%s.
383 /// Do this even before updating all other ops()!
384 Def* set_type(const Def*);
385 ///@}
386
387 /// @name deps
388 /// All *dependencies* of a Def and includes:
389 /// * Def::type() (if not `nullptr`) and
390 /// * the other Def::ops() (only included, if Def::is_set()) in this order.
391 ///@{
392 Defs deps() const noexcept;
393 const Def* dep(size_t i) const noexcept { return deps()[i]; }
394 size_t num_deps() const noexcept { return deps().size(); }
395 ///@}
396
397 /// @name has_dep
398 /// Checks whether one Def::deps() contains specific elements defined in Dep.
399 /// This works up to the next *mutable*.
400 /// For example, consider the Tuple `tup`: `(?, lam (x: Nat) = y)`:
401 /// ```
402 /// bool has_hole = tup->has_dep(Dep::Hole); // true
403 /// bool has_mut = tup->has_dep(Dep::Mut); // true
404 /// bool has_var = tup->has_dep(Dep::Var); // false - y is contained in another mutable
405 /// ```
406 ///@{
407 Dep dep() const noexcept { return Dep(dep_); }
408 bool has_dep() const noexcept { return dep_ != 0; }
409 bool has_dep(Dep d) const noexcept { return fe::has_flag(dep(), d); }
410 ///@}
411
412 /// @name proj
413 /// @anchor proj
414 /// Splits this Def via Extract%s or directly accessing the Def::ops in the case of Sigma%s or Arr%ays.
415 /// ```
416 /// std::array<const Def*, 2> ab = def->projs<2>();
417 /// std::array<u64, 2> xy = def->projs<2>([](auto def) { return Lit::as(def); });
418 /// auto [a, b] = def->projs<2>();
419 /// auto [x, y] = def->projs<2>([](auto def) { return Lit::as(def); });
420 /// fe::Vector<const Def*> projs1 = def->projs(); // "projs1" has def->num_projs() many elements
421 /// fe::Vector<const Def*> projs2 = def->projs(n);// "projs2" has n elements - asserts if incorrect
422 /// // same as above but applies Lit::as<nat_t>(def) to each element
423 /// fe::Vector<const Lit*> lits1 = def->projs( [](auto def) { return Lit::as(def); });
424 /// fe::Vector<const Lit*> lits2 = def->projs(n, [](auto def) { return Lit::as(def); });
425 /// ```
426 ///@{
427
428 /// Yields Def::arity(), if it is a Lit, or `1` otherwise.
429 nat_t num_projs() const;
430 nat_t num_tprojs() const; ///< As above but yields 1, if Flags::scalarize_threshold is exceeded.
431
432 /// Similar to World::extract while assuming an arity of @p a, but also works on Sigma%s and Arr%ays.
433 const Def* proj(nat_t a, nat_t i) const;
434 const Def* proj(nat_t i) const { return proj(num_projs(), i); } ///< As above but takes Def::num_projs as arity.
435 const Def* tproj(nat_t i) const { return proj(num_tprojs(), i); } ///< As above but takes Def::num_tprojs.
436
437 /// Splits this Def via Def::proj%ections into an Array (if `A == std::dynamic_extent`) or `std::array` (otherwise).
438 /// Applies @p f to each element.
439 template<nat_t A = std::dynamic_extent, class F>
440 auto projs(F f) const {
441 using R = std::decay_t<decltype(f(this))>;
442 if constexpr (A == std::dynamic_extent) {
443 return projs(num_projs(), f);
444 } else {
445 std::array<R, A> array;
446 for (nat_t i = 0; i != A; ++i)
447 array[i] = f(proj(A, i));
448 return array;
449 }
450 }
451
452 template<class F>
453 auto tprojs(F f) const {
454 return projs(num_tprojs(), f);
455 }
456
457 template<class F>
458 auto projs(nat_t a, F f) const {
459 using R = std::decay_t<decltype(f(this))>;
460 return fe::Vector<R>(a, [&](nat_t i) { return f(proj(a, i)); });
461 }
462 template<nat_t A = std::dynamic_extent>
463 auto projs() const {
464 return projs<A>([](const Def* def) { return def; });
465 }
466 auto tprojs() const {
467 return tprojs([](const Def* def) { return def; });
468 }
469 auto projs(nat_t a) const {
470 return projs(a, [](const Def* def) { return def; });
471 }
472 ///@}
473
474 /// @name var
475 /// @anchor var
476 /// Retrieve Var for *mutables*.
477 /// @see @ref proj
478 ///@{
480 const Def* var(); ///< Not necessarily a Var: E.g., if the return type is `[]`, this will yield `()`.
481 const Def* var_type(); ///< If `this` is a binder, compute the type of its Var%iable.
482
483 const Var* has_var() { return var_; } ///< Only returns not `nullptr`, if Var of this mutable has ever been created.
484 /// As above if `this` is a *mutable*.
485 const Var* has_var() const { return mut_ ? var_ : nullptr; }
486
487 /// Is `this` a mutable that introduces a Var?
488 /// @returns `{nullptr, nullptr}` otherwise.
489 template<class D = Def>
490 std::pair<D*, const Var*> isa_binder() const {
491 if (auto mut = isa_mut<D>()) {
492 if (auto var = mut->has_var()) return {mut, var};
493 }
494 return {nullptr, nullptr};
495 }
496 ///@}
497
498 /// @name Free Vars and Muts
499 /// MimIR splits the free-variable analysis into a *local* and a *global* layer:
500 /// * local_muts() / local_vars() only look at the *immutable* fan-out and are cheap, cached, and hash-consed.
501 /// * free_vars() close over the *mutable* boundary as well and are the actual set of free Var%s.
502 /// They are computed on demand via a fixed-point iteration and cached in mutables.
503 /// Mutating a mutable transitively invalidates these caches by following users().
504 ///@{
505
506 /// Mutables reachable by following *immutable* deps(); `mut->local_muts()` is by definition the set `{ mut }`.
507 Muts local_muts() const {
508 if (auto mut = isa_mut()) return Muts(mut);
509 return muts_;
510 }
511
512 /// Var%s reachable by following *immutable* deps().
513 /// @note `var->local_vars()` is by definition the set `{ var }`.
514 Vars local_vars() const { return mut_ ? Vars() : vars_; }
515
516 /// Global set of free Var%s: extends local_vars() by transitively following *mutables* as well.
517 /// @note On a *mutable* this simply forwards to the caching non-`const` overload below.
518 Vars free_vars() const;
519 Vars free_vars(); ///< As above but drives (and caches) the fixed-point iteration for *mutables*.
520 Muts users() { return muts_; } ///< Set of mutables where this mutable is locally referenced.
521 bool is_open() const { return has_free_vars(); } ///< Same as has_free_vars().
522 bool is_closed() const; ///< Same as `!has_free_vars()`.
523
524 /// Immutable that contains neither mutables nor Var%s.
525 bool is_ground() const { return !mut_ && local_muts().empty() && local_vars().empty(); }
526
527 /// Transitively walks up free_vars() till the outermoust binder has been found.
528 /// @returns `nullptr`, if is_closed() and not a mutable.
529 Def* outermost_binder() const;
530
531 /// Does @p this nest @p mut?
532 /// The relation is strict: `f->nests(f)` is `false`.
533 bool nests(Def* mut);
534 /// Does @p this nest @p def?
535 /// Also strict: a @p def that only uses @p this%'s own Var sits at @p this%'s level and is *not* nested.
536 bool nests(const Def* def);
537 ///@}
538
539 /// @name free_vars predicates
540 /// `free_vars()` of an *immutable* is **not** cached: it merges `free_vars()` of every local_muts() entry on
541 /// every call, and each merge allocates, hashes, and probes the pool.
542 /// Since free_vars() is a union, any predicate over it distributes over that union - so these answer the
543 /// question without ever materializing the merged set.
544 /// Prefer them over `free_vars().contains(...)` / `.empty()` / `has_intersection(...)`.
545 ///@{
546 bool has_free_var(const Var*) const; ///< Same as `free_vars().contains(var)`.
547 bool has_free_vars() const; ///< Same as `!free_vars().empty()`.
548 bool has_free_vars_in(Vars) const; ///< Same as `vars.has_intersection(free_vars())`.
549 ///@}
550
551 /// @name external
552 ///@{
553 bool is_external() const noexcept { return external_; }
554 void externalize();
555 void internalize();
556 void transfer_external(Def* to);
557 bool is_annex() const noexcept { return annex_; }
558 ///@}
559
560 /// @name dirty
561 /// Scratch bit for Phase%s to mark muts that need re-examination.
562 /// @see Phase::taint
563 ///@{
564 bool is_dirty() const noexcept { return dirty_; }
565 void dirty(bool dirty = true) noexcept { dirty_ = dirty; }
566 ///@}
567
568 /// @name Casts
569 /// @see @ref cast_builtin
570 ///@{
571 bool is_mutable() const noexcept { return mut_; }
572
573 // clang-format off
574 template<class T = Def> const T* isa_imm() const { return isa_mut<T, true>(); }
575 template<class T = Def> const T* as_imm() const { return as_mut<T, true>(); }
576 // clang-format on
577
578 /// If `this` is *mutable*, it will cast `const`ness away and perform a `dynamic_cast` to @p T.
579 template<class T = Def, bool invert = false>
580 T* isa_mut() const {
581 if constexpr (std::is_same_v<T, Def>)
582 return mut_ ^ invert ? const_cast<Def*>(this) : nullptr;
583 else
584 return mut_ ^ invert ? const_cast<Def*>(this)->template isa<T>() : nullptr;
585 }
586
587 /// Asserts that `this` is a *mutable*, casts `const`ness away and performs a `static_cast` to @p T.
588 template<class T = Def, bool invert = false>
589 T* as_mut() const {
590 assert(mut_ ^ invert);
591 if constexpr (std::is_same_v<T, Def>)
592 return const_cast<Def*>(this);
593 else
594 return const_cast<Def*>(this)->template as<T>();
595 }
596
597 /// Like Def::as_mut but - instead of merely asserting in `Debug` builds - throws via fe::throwf when the cast
598 /// fails; the mutable counterpart of fe::RuntimeCast::expect (which Def inherits for the general case).
599 /// @p fmt / @p args describe what was expected; a plain string works, as does a format string plus arguments.
600 template<class T = Def, class... Args>
601 T* expect_mut(std::format_string<Args...> fmt, Args&&... args) const {
602 if (auto res = isa_mut<T>()) return res;
603 fe::throwf("expected {}, but got `{}`", std::format(fmt, std::forward<Args>(args)...), this);
604 }
605 ///@}
606
607 /// @name Dbg Getters
608 ///@{
609 Dbg dbg() const; ///< Looks up Def::dbg_ in Driver::dbg.
610 DbgKey dbg_key() const { return dbg_; } ///< Cheap handle for `other->set(this->dbg_key())`.
611 Loc loc() const { return dbg().loc(); }
612 Sym sym() const { return dbg().sym(); }
613 std::string unique_name() const; ///< name + "_" + Def::gid
614 ///@}
615
616 /// @name Dbg Setters
617 /// Every subclass `S` of Def has the same setters that return `S*`/`const S*` via the mixin Setters.
618 ///@{
619 // clang-format off
620 template<bool Ow = false> const Def* set(Loc l) const { if (auto d = dbg(); Ow || !d.loc()) set_dbg(d.set(l)); return this; }
621 template<bool Ow = false> Def* set(Loc l) { if (auto d = dbg(); Ow || !d.loc()) set_dbg(d.set(l)); return this; }
622 template<bool Ow = false> const Def* set(Sym s) const { if (auto d = dbg(); Ow || !d.sym()) set_dbg(d.set(s)); return this; }
623 template<bool Ow = false> Def* set(Sym s) { if (auto d = dbg(); Ow || !d.sym()) set_dbg(d.set(s)); return this; }
624 template<bool Ow = false> const Def* set( std::string s) const { set<Ow>(sym(std::move(s))); return this; }
625 template<bool Ow = false> Def* set( std::string s) { set<Ow>(sym(std::move(s))); return this; }
626 template<bool Ow = false> const Def* set(Loc l, Sym s ) const { set<Ow>(l); set<Ow>(s); return this; }
627 template<bool Ow = false> Def* set(Loc l, Sym s ) { set<Ow>(l); set<Ow>(s); return this; }
628 template<bool Ow = false> const Def* set(Loc l, std::string s) const { set<Ow>(l); set<Ow>(sym(std::move(s))); return this; }
629 template<bool Ow = false> Def* set(Loc l, std::string s) { set<Ow>(l); set<Ow>(sym(std::move(s))); return this; }
630 template<bool Ow = false> const Def* set(Dbg d) const { set_dbg_(d, Ow); return this; }
631 template<bool Ow = false> Def* set(Dbg d) { set_dbg_(d, Ow); return this; }
632 /// Adopts the Dbg behind @p key - just copies the interned index, so nothing is re-interned.
633 /// Prefer `a->set(b->dbg_key())` over `a->set(b->dbg())`.
634 template<bool Ow = false> const Def* set(DbgKey key) const { set_dbg_key_(key, Ow); return this; }
635 template<bool Ow = false> Def* set(DbgKey key) { set_dbg_key_(key, Ow); return this; }
636 // clang-format on
637 ///@}
638
639 /// @name debug_prefix/suffix
640 /// Prepends/Appends a prefix/suffix to Def::name - but only in `Debug` build.
641 ///@{
642#ifndef NDEBUG
643 const Def* debug_prefix(std::string) const;
644 const Def* debug_suffix(std::string) const;
645#else
646 const Def* debug_prefix(std::string) const { return this; }
647 const Def* debug_suffix(std::string) const { return this; }
648#endif
649 ///@}
650
651 /// @name Rebuild
652 ///@{
653 /// Tries to make an immutable from a mutable.
654 /// This usually works if the mutable isn't recursive and its var isn't used.
655 const Def* immutabilize();
656 bool is_immutabilizable();
657
658 /// @see World::reduce
659 template<size_t N = std::dynamic_extent>
660 constexpr auto reduce(const Def* arg) const {
661 return reduce_(arg).span<N>();
662 }
663
664 /// First Def::op that needs to be dealt with during reduction; e.g. for a Pi we don't reduce the Pi::dom.
665 /// @see World::reduce
666 size_t reduction_offset() const noexcept;
667 ///@}
668
669 /// @name Type Checking
670 ///@{
671
672 /// Checks whether the `i`th operand can be set to `def`.
673 /// The method returns a possibly updated version of `def` (e.g. where Hole%s have been resolved).
674 /// This is the actual `def` that will be set as the `i`th operand.
675 const Def* check(size_t i, const Def* def);
676
677 /// After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
678 /// The method returns a possibly updated version of its type (e.g. where Hole%s have been resolved).
679 /// If different from Def::type, it will update its Def::type to a Def::zonk%ed version of that.
680 const Def* check();
681
682 /// Yields `true`, if Def::local_muts() contain a Hole that is set.
683 /// Rewriting (Def::zonk%ing) will resolve the Hole to its operand.
684 bool needs_zonk() const;
685
686 /// If Hole%s have been filled, reconstruct the program without them.
687 /// Only goes up to but excluding other mutables.
688 /// @see https://stackoverflow.com/questions/31889048/what-does-the-ghc-source-mean-by-zonk
689 const Def* zonk() const;
690
691 /// If *mutable*, zonk()%s all ops and tries to immutabilize it; otherwise just zonk.
692 const Def* zonk_mut() const;
693 ///@}
694
695 /// zonk%s all @p defs and returns a new DefVec.
696 static DefVec zonk(Defs defs);
697
698 /// @name dump
699 /// @note While this output uses Mim syntax, it does usually **not** produce programs that can be read back.
700 /// It uses an unscheduled visiting algorithm, and is only meant for debugging purposes.
701 ///@{
702 void dump() const;
703 void dump(int max) const;
704 void write(int max) const;
705 void write(int max, const char* file) const;
706 std::ostream& stream(std::ostream&, int max) const;
707 ///@}
708
709 /// @name Syntactic Comparison
710 /// Establishes an arbitrary but deterministic total order on Def%s that is stable across runs.
711 ///@{
712 enum class Cmp {
713 L, ///< Less
714 G, ///< Greater
715 E, ///< Equal
716 U, ///< Unknown
717 };
718 [[nodiscard]] static Cmp cmp(const Def* a, const Def* b);
719 [[nodiscard]] static bool less(const Def* a, const Def* b);
720 [[nodiscard]] static bool greater(const Def* a, const Def* b);
721 ///@}
722
723 /// @name dot
724 /// Streams dot to @p os, configured via @p cfg (see DotConfig).
725 ///@{
726 void dot(std::ostream& os, DotConfig cfg = {}) const;
727 /// Same as above but write to @p file or `std::cout` if @p file is `nullptr`.
728 void dot(const char* file = nullptr, DotConfig cfg = {}) const;
729 void dot(const std::string& file, DotConfig cfg = {}) const { return dot(file.c_str(), cfg); }
730 ///@}
731
732protected:
733 /// @name Wrappers for World::sym
734 /// These are here to have Def::set%ters inline without including `mim/world.h`.
735 ///@{
736 Sym sym(const char*) const;
737 Sym sym(std::string_view) const;
738 Sym sym(std::string) const;
739 void set_dbg(Dbg) const; ///< Interns @p dbg via Driver::dbg and stores the key in Def::dbg_.
740 void set_dbg_(Dbg, bool ow) const; ///< Backs Def::set(Dbg).
741 void set_dbg_key_(DbgKey, bool ow) const; ///< Backs Def::set(DbgKey).
742 ///@}
743
744private:
745 Defs reduce_(const Def* arg) const;
746
747 void watch() const; ///< Trips World::watchpoints in `MIM_ENABLE_CHECKS` builds; a no-op otherwise.
748 Def* finalize(); ///< Runs Def::check once the last op has been set; @see @ref set_ops.
749
750 template<bool init>
751 Vars free_vars(World&, bool&, u32);
752 void invalidate();
753 const Def** ops_ptr() const {
754 return reinterpret_cast<const Def**>(reinterpret_cast<char*>(const_cast<Def*>(this + 1)));
755 }
756 bool equal(const Def* other) const;
757 bool nests(Def*, MutSet&);
758
759 template<Cmp>
760 [[nodiscard]] static bool cmp_(const Def* a, const Def* b);
761
762protected:
763 union {
764 NormalizeFn normalizer_; ///< Axm only: Axm%s use this member to store their normalizer.
765 const Axm* axm_; ///< App only: Curried App%s of Axm%s use this member to propagate the Axm.
766 const Var* var_; ///< Mutable only: Var of a mutable.
767 Def* binder_; ///< Var only: the binder this Var refers to (*not* an official op).
768 mutable World* world_;
769 };
773
774private:
775 Node node_; // node_t is u8; the four flags + dep_ below fill the remaining byte of this word
776 bool mut_ : 1;
777 bool external_ : 1;
778 mutable bool annex_ : 1;
779 bool dirty_ : 1;
780 unsigned dep_ : 4;
781 u32 mark_ = 0;
782 u32 gid_;
783 u32 num_ops_;
784 size_t hash_;
785 Vars vars_; // Mutable: local vars; Immutable: free vars.
786 Muts muts_; // Immutable: local_muts; Mutable: users;
787 /// Handle into the Driver's Dbg table rather than a full Dbg: this keeps `sizeof(Def)` down by
788 /// 20 bytes on *every* node, and Dbg%s are shared roughly 10:1 in practice.
789 mutable DbgKey dbg_;
790#ifndef NDEBUG
791 u32 curr_op_ = 0; // an operand index, so u32 suffices (num_ops_ is u32 too); shares dbg_'s 8-byte slot
792#endif
793 mutable const Def* type_;
794
795 friend struct DefKey;
796 friend class World;
797 friend void swap(World&, World&) noexcept;
798 friend std::ostream& operator<<(std::ostream&, const Def*);
799};
800
801inline u32 DefKey::key(const Def* d) noexcept { return d->gid_; }
802
803/// Def must never become polymorphic: a vptr costs 8 bytes on *every* node in the World, and Def::ops_ptr
804/// hands out the operands at `this + 1`, so the vptr would also shift them. Def carries its own Def::node()
805/// tag and dispatches on it instead - see the `dispatch` section in `def.cpp`.
806/// @note A *subclass* growing a `virtual` is caught by the `sizeof(Def) == sizeof(T)` assert in World::allocate.
807static_assert(!std::is_polymorphic_v<Def>, "Def must not have a vtable; dispatch on Def::node() instead");
808
809/// Table-driven and defined here instead of in `def.cpp`, so that Def::is_form \& friends stay inlinable:
810/// `libmim` is a shared object, so an out-of-line judge() would be an opaque PLT call from every other TU.
811inline Judge Def::judge() const noexcept {
812 static constexpr Judge Judges[Num_Nodes] = {
813#define CODE(node, judge) judge,
815#undef CODE
816 };
817 return Judges[node_t(node_)];
818}
819
820/// A variable introduced by a binder (mutable).
821/// @note Var will keep its type_ field as `nullptr`.
822/// Instead, Def::type() and Var::type() will compute the type via Def::var_type().
823/// The reason is that the type could need a Def::zonk().
824/// But we don't want to have several Var%s that belong to the same binder.
825class Var : public Def, public Setters<Var> {
826private:
827 Var(Def* mut)
828 : Def(Node, mut) {}
829
830public:
831 using Setters<Var>::set;
832
833 /// The binder of this Var.
834 /// It is *not* an official Def::op but stored in Def::binder_, so it is out of the operand graph but still hashed.
835 Def* binder() const { return binder_; }
836 const Def* type() const { return binder()->var_type(); }
837
838 static constexpr auto Node = mim::Node::Var;
839 static constexpr size_t Num_Ops = 0;
840
841private:
842 friend class World;
843};
844
845class Univ : public Def, public Setters<Univ> {
846public:
847 using Setters<Univ>::set;
848 static constexpr auto Node = mim::Node::Univ;
849 static constexpr size_t Num_Ops = 0;
850
851private:
852 Univ(World& world)
853 : Def(&world, Node, nullptr, Defs{}, 0) {}
854
855 friend class World;
856};
857
858class UMax : public Def, public Setters<UMax> {
859public:
860 using Setters<UMax>::set;
861 static constexpr auto Node = mim::Node::UMax;
862 static constexpr size_t Num_Ops = std::dynamic_extent;
863
864 enum Sort { Univ, Kind, Type, Term };
865
866private:
867 UMax(World&, Defs ops);
868
869 friend class World;
870};
871
872class UInc : public Def, public Setters<UInc> {
873private:
874 UInc(const Def* op, level_t offset)
875 : Def(Node, op->type()->as<Univ>(), {op}, offset) {}
876
877public:
878 using Setters<UInc>::set;
879
880 /// @name ops
881 ///@{
882 const Def* op() const { return Def::op(0); }
883 level_t offset() const { return flags(); }
884 ///@}
885
886 static constexpr auto Node = mim::Node::UInc;
887 static constexpr size_t Num_Ops = 1;
888
889private:
890 friend class World;
891};
892
893class Type : public Def, public Setters<Type> {
894private:
895 Type(const Def* level)
896 : Def(Node, nullptr, {level}, 0) {}
897
898public:
899 using Setters<Type>::set;
900
901 /// @name ops
902 ///@{
903 const Def* level() const { return op(0); }
904 ///@}
905
906 static constexpr auto Node = mim::Node::Type;
907 static constexpr size_t Num_Ops = 1;
908
909private:
910 friend class World;
911};
912
913class Lit : public Def, public Setters<Lit> {
914private:
915 Lit(const Def* type, flags_t val)
916 : Def(Node, type, Defs{}, val) {}
917
918public:
919 using Setters<Lit>::set;
920
921 /// @name Get actual Constant
922 ///@{
923 template<class T = flags_t>
924 T get() const {
925 static_assert(sizeof(T) <= 8);
926 return fe::bitcast_resize<T>(flags_);
927 }
928 ///@}
929
930 using Def::as;
931 using Def::isa;
932
933 /// @name Casts
934 ///@{
935 /// @see @ref cast_lit
936 template<class T = nat_t>
937 static std::optional<T> isa(const Def* def) {
938 if (!def) return {};
939 if (auto lit = def->isa<Lit>()) return lit->get<T>();
940 return {};
941 }
942 template<class T = nat_t>
943 static T as(const Def* def) {
944 return def->as<Lit>()->get<T>();
945 }
946 /// Like Lit::as but throws a formatted mim::error instead of merely asserting in `Debug`; see Def::expect.
947 template<class T = nat_t, class... Args>
948 static T expect(const Def* def, std::format_string<Args...> fmt, Args&&... args) {
949 if (auto res = isa<T>(def)) return *res;
950 fe::throwf("expected {}, but got `{}`", std::format(fmt, std::forward<Args>(args)...), def);
951 }
952 ///@}
953
954 static constexpr auto Node = mim::Node::Lit;
955 static constexpr size_t Num_Ops = 0;
956
957private:
958 friend class World;
959};
960
961class Nat : public Def, public Setters<Nat> {
962public:
963 using Setters<Nat>::set;
964 static constexpr auto Node = mim::Node::Nat;
965 static constexpr size_t Num_Ops = 0;
966
967private:
968 Nat(World& world);
969
970 friend class World;
971};
972
973/// A built-in constant of type `Nat -> *`.
974class Idx : public Def, public Setters<Idx> {
975private:
976 Idx(const Def* type)
977 : Def(Node, type, Defs{}, 0) {}
978
979public:
980 using Setters<Idx>::set;
981 using Def::as;
982 using Def::isa;
983
984 /// @name isa
985 ///@{
986
987 /// Checks if @p def is a `Idx s` and returns `s` or `nullptr` otherwise.
988 static const Def* isa(const Def* def);
989 static const Def* as(const Def* def) {
990 auto res = isa(def);
991 assert(res);
992 return res;
993 }
994 static std::optional<nat_t> isa_lit(const Def* def);
995 static nat_t as_lit(const Def* def) {
996 auto res = isa_lit(def);
997 assert(res.has_value());
998 return *res;
999 }
1000 ///@}
1001
1002 /// @name Convert between Idx::isa and bitwidth and vice versa
1003 ///@{
1004 // clang-format off
1005 static constexpr nat_t bitwidth2size(nat_t n) { assert(n != 0); return n == 64 ? 0 : (1_n << n); }
1006 static constexpr nat_t size2bitwidth(nat_t n) { return n == 0 ? 64 : std::bit_width(n - 1_n); }
1007 // clang-format on
1008 static std::optional<nat_t> size2bitwidth(const Def* size);
1009
1010 /// Yields the bit width of the `Idx` @p type or throws a formatted mim::error - instead of yielding
1011 /// std::nullopt or dereferencing an unchecked std::optional - if @p type is not an `Idx` of statically known
1012 /// size; see Def::expect.
1013 template<class... Args>
1014 static nat_t expect_bitwidth(const Def* type, std::format_string<Args...> fmt, Args&&... args) {
1015 if (auto size = isa(type))
1016 if (auto w = size2bitwidth(size)) return *w;
1017 fe::throwf("expected {}, but got `{}`", std::format(fmt, std::forward<Args>(args)...), type);
1018 }
1019 ///@}
1020
1021 static constexpr auto Node = mim::Node::Idx;
1022 static constexpr size_t Num_Ops = 0;
1023
1024private:
1025 friend class World;
1026};
1027
1028/// Used as intermediate value during optimizatinos such as Analysis.
1029/// @note Def::ops() are hashed as normal but they do **not** contribute to Def::local_vars(), nor Def::local_muts() and
1030/// hence not to Def::free_vars(). This is by design as those ops typically are some meta information to memoize certain
1031/// things that do not carry semantic information per se.
1032class Proxy : public Def, public Setters<Proxy> {
1033private:
1034 Proxy(const Def* type, flags_t tag, Defs ops)
1035 : Def(Node, type, ops, tag) {}
1036
1037public:
1038 using Setters<Proxy>::set;
1039
1040 /// @name Getters
1041 ///@{
1042 flags_t tag() const { return flags_; }
1043 ///@}
1044
1045 template<flags_t Tag>
1046 static const Proxy* isa(const Def* def) {
1047 if (auto proxy = def->isa<Proxy>(); proxy && proxy->tag() == Tag) return proxy;
1048 return nullptr;
1049 }
1050
1051 static constexpr auto Node = mim::Node::Proxy;
1052 static constexpr size_t Num_Ops = std::dynamic_extent;
1053
1054private:
1055 friend class World;
1056};
1057
1058/// @deprecated A global variable in the data segment.
1059/// A Global may be mutable or immutable.
1060/// @deprecated Will be removed.
1061class Global : public Def, public Setters<Global> {
1062private:
1063 Global(const Def* type, bool is_mutable)
1064 : Def(Node, type, 1, is_mutable) {}
1065
1066public:
1067 using Setters<Global>::set;
1068
1069 /// @name ops
1070 ///@{
1071 const Def* init() const { return op(0); }
1072 void set(const Def* init) { Def::set(0, init); }
1073 ///@}
1074
1075 /// @name type
1076 ///@{
1077 const App* type() const;
1078 const Def* alloced_type() const;
1079 ///@}
1080
1081 /// @name Getters
1082 ///@{
1083 bool is_mutable() const { return flags(); }
1084 ///@}
1085
1086 static constexpr auto Node = mim::Node::Global;
1087 static constexpr size_t Num_Ops = 1;
1088
1089private:
1090 friend class World;
1091};
1092
1093// Def - hot inline definitions
1094// These need Univ, Type, Var, and Lit to be complete, so they live here rather than in the class body.
1095// They are tiny and called millions of times, and `libmim` is a shared object - out of line they would be
1096// opaque PLT calls in every other TU.
1097inline World& Def::world() const noexcept {
1098 // Walks up the type chain till it bottoms out in Univ - the only node that actually stores its World.
1099 // clang-format off
1100 for (auto def = this;;) {
1101 switch (def->node_) {
1102 case Node::Univ: return *def->world_;
1103 case Node::Type: return *def->op(0)->type()->as<Univ>()->world_; // op(0) is Type::level
1104 case Node::Var: def = def->binder_; break; // a Var has no type_ of its own
1105 default: def = def->type_; break;
1106 }
1107 }
1108 // clang-format on
1109}
1110
1111inline const Def* Def::type() const noexcept {
1112 if (auto var = isa<Var>()) return var->binder()->var_type();
1113 return type_;
1114}
1115
1116inline bool Def::equal(const Def* other) const {
1117 // Univ is a singleton and mutables are never hash-consed, so for those identity *is* equality.
1118 if (mut_ || other->mut_ || node_ == Node::Univ) return this == other;
1119
1120 // A Var carries no ops and flags == 0, so it is identified solely by its binder
1121 if (auto var = isa<Var>()) return other->isa<Var>() && var->binder() == other->as<Var>()->binder();
1122
1123 bool result = this->node() == other->node() && this->flags() == other->flags()
1124 && this->num_ops() == other->num_ops() && this->type() == other->type();
1125
1126 for (size_t i = 0, e = num_ops(); result && i != e; ++i)
1127 result &= this->op(i) == other->op(i);
1128
1129 return result;
1130}
1131
1132inline nat_t Def::num_projs() const { return Lit::isa(arity()).value_or(1); }
1133
1134/// Def::unfold_type of @p def for a diagnostic - Univ is the one Def that has no type at all.
1135/// Streams lazily so that it still renders under the PlainNames guard Error::msg formats within.
1136inline auto type_of(const Def* def) {
1137 return fe::StreamFn{[def](std::ostream& os) -> std::ostream& {
1138 if (auto t = def->unfold_type()) return os << t;
1139 return os << "<no type>";
1140 }};
1141}
1142
1143} // namespace mim
1144
1145#ifndef DOXYGEN // clang-format off
1146/// Format any pointer to a `mim::Def` (or subclass) via its `operator<<`.
1147template<class T> requires std::derived_from<T, mim::Def> struct std::formatter< T*> : fe::ostream_formatter {};
1148template<class T> requires std::derived_from<T, mim::Def> struct std::formatter<const T*> : fe::ostream_formatter {};
1149template<> struct std::formatter<mim::Muts> : fe::ostream_formatter {};
1150template<> struct std::formatter<mim::Vars> : fe::ostream_formatter {};
1151#endif // clang-format on
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
size_t num_deps() const noexcept
Definition def.h:394
const Def * zonk_mut() const
If mutable, zonk()s all ops and tries to immutabilize it; otherwise just zonk.
Definition check.cpp:27
const Def * set(Dbg d) const
Definition def.h:630
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
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
Definition def.h:589
const Var * has_var() const
As above if this is a mutable.
Definition def.h:485
void dump() const
Definition dump.cpp:577
void dirty(bool dirty=true) noexcept
Definition def.h:565
bool has_dep() const noexcept
Definition def.h:408
Defs deps() const noexcept
Definition def.cpp:468
auto projs() const
Definition def.h:463
u8 trip_
Definition def.h:772
bool is_elim() const noexcept
Definition def.h:322
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
auto projs(nat_t a, F f) const
Definition def.h:458
fe::Error & error() const noexcept
Definition def.cpp:400
bool is_intro() const noexcept
Definition def.h:321
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
bool has_dep(Dep d) const noexcept
Definition def.h:409
const Def * set(std::string s) const
Definition def.h:624
Dep dep() const noexcept
Definition def.h:407
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:580
constexpr u32 mark() const noexcept
Used internally by free_vars().
Definition def.h:295
auto projs(nat_t a) const
Definition def.h:469
u8 curry_
Definition def.h:771
friend struct DefKey
Definition def.h:795
auto tprojs() const
Definition def.h:466
Judge judge() const noexcept
Def must never become polymorphic: a vptr costs 8 bytes on every node in the World,...
Definition def.h:811
void externalize()
Definition def.cpp:607
friend std::ostream & operator<<(std::ostream &, const Def *)
This will stream def as an operand.
Definition dump.cpp:548
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
void dot(std::ostream &os, DotConfig cfg={}) const
Definition dot.cpp:203
DbgKey dbg_key() const
Cheap handle for other->set(this->dbg_key()).
Definition def.h:610
bool is_immutabilizable()
Definition def.cpp:140
std::pair< D *, const Var * > isa_binder() const
Is this a mutable that introduces a Var?
Definition def.h:490
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
const Def * proj(nat_t i) const
As above but takes Def::num_projs as arity.
Definition def.h:434
bool has_free_vars() const
Same as !free_vars().empty().
Definition def.cpp:277
Def * set(DbgKey key)
Definition def.h:635
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
Definition def.h:440
bool is_open() const
Same as has_free_vars().
Definition def.h:521
constexpr size_t hash() const noexcept
Definition def.h:296
friend class World
Definition def.h:796
bool is_dirty() const noexcept
Definition def.h:564
T * expect_mut(std::format_string< Args... > fmt, Args &&... args) const
Like Def::as_mut but - instead of merely asserting in Debug builds - throws via fe::throwf when the c...
Definition def.h:601
bool has_free_var(const Var *) const
Same as free_vars().contains(var).
Definition def.cpp:273
bool is_form() const noexcept
Definition def.h:320
void set_dbg_(Dbg, bool ow) const
Backs Def::set(Dbg).
Definition def.cpp:435
Def * set(Dbg d)
Definition def.h:631
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
Definition def.h:507
bool is_ground() const
Immutable that contains neither mutables nor Vars.
Definition def.h:525
Def * set(Sym s)
Definition def.h:623
const Def * debug_suffix(std::string) const
Definition def.cpp:495
const Def * set(Sym s) const
Definition def.h:622
Def * set(Loc l)
Definition def.h:621
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
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
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
void internalize()
Definition def.cpp:608
const Def * set(Loc l, Sym s) const
Definition def.h:626
static bool less(const Def *a, const Def *b)
Definition def.cpp:542
Def * set(std::string s)
Definition def.h:625
bool is_meta() const noexcept
Definition def.h:323
static bool greater(const Def *a, const Def *b)
Definition def.cpp:543
void dot(const std::string &file, DotConfig cfg={}) const
Definition def.h:729
const Def * set(Loc l) const
Definition def.h:620
Loc loc() const
Definition def.h:611
void write(int max) const
Definition dump.cpp:585
auto tprojs(F f) const
Definition def.h:453
const Def * var()
Not necessarily a Var: E.g., if the return type is [], this will yield ().
Definition def.cpp:226
Def * set(Loc l, std::string s)
Definition def.h:629
const T * as_imm() const
Definition def.h:575
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
nat_t num_projs() const
Yields Def::arity(), if it is a Lit, or 1 otherwise.
Definition def.h:1132
std::ostream & stream(std::ostream &, int max) const
Definition dump.cpp:557
flags_t flags_
Definition def.h:770
const Def * immutabilize()
Definition def.cpp:555
friend void swap(World &, World &) noexcept
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
constexpr auto reduce(const Def *arg) const
Definition def.h:660
const Def * set(Loc l, std::string s) const
Definition def.h:628
const T * isa_imm() const
Definition def.h:574
bool needs_zonk() const
Yields true, if Def::local_muts() contain a Hole that is set.
Definition check.cpp:12
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
const Def * set(DbgKey key) const
Adopts the Dbg behind key - just copies the interned index, so nothing is re-interned.
Definition def.h:634
Def * set(Loc l, Sym s)
Definition def.h:627
Dbg dbg() const
Looks up Def::dbg_ in Driver::dbg.
Definition def.cpp:430
const Def * tproj(nat_t i) const
As above but takes Def::num_tprojs.
Definition def.h:435
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
bool is_annex() const noexcept
Definition def.h:557
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
const Def * check()
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Definition check.cpp:454
Some "global" variables needed all over the place.
Definition driver.h:63
const Def * init() const
Definition def.h:1071
void set(const Def *init)
Definition def.h:1072
const Def * alloced_type() const
Definition def.cpp:670
friend class World
Definition def.h:1090
bool is_mutable() const
Definition def.h:1083
const App * type() const
Definition def.cpp:669
static constexpr size_t Num_Ops
Definition def.h:1087
static constexpr auto Node
Definition def.h:1086
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:16
static constexpr auto Node
Definition def.h:1021
static nat_t as_lit(const Def *def)
Definition def.h:995
static constexpr nat_t size2bitwidth(nat_t n)
Definition def.h:1006
static constexpr nat_t bitwidth2size(nat_t n)
Definition def.h:1005
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:645
friend class World
Definition def.h:1025
static nat_t expect_bitwidth(const Def *type, std::format_string< Args... > fmt, Args &&... args)
Yields the bit width of the Idx type or throws a formatted mim::error - instead of yielding std::null...
Definition def.h:1014
static std::optional< nat_t > isa_lit(const Def *def)
Definition def.cpp:653
static constexpr size_t Num_Ops
Definition def.h:1022
static const Def * as(const Def *def)
Definition def.h:989
static constexpr auto Node
Definition def.h:954
static std::optional< T > isa(const Def *def)
Definition def.h:937
friend class World
Definition def.h:958
T get() const
Definition def.h:924
static T as(const Def *def)
Definition def.h:943
static constexpr size_t Num_Ops
Definition def.h:955
static T expect(const Def *def, std::format_string< Args... > fmt, Args &&... args)
Like Lit::as but throws a formatted mim::error instead of merely asserting in Debug; see Def::expect.
Definition def.h:948
friend class World
Definition def.h:970
static constexpr auto Node
Definition def.h:964
static constexpr size_t Num_Ops
Definition def.h:965
Used as intermediate value during optimizatinos such as Analysis.
Definition def.h:1032
static constexpr size_t Num_Ops
Definition def.h:1052
flags_t tag() const
Definition def.h:1042
friend class World
Definition def.h:1055
static constexpr auto Node
Definition def.h:1051
static const Proxy * isa(const Def *def)
Definition def.h:1046
CRTP-based mixin to declare setters for Def::loc & Def::name using a covariant return type.
Definition def.h:209
const P * set(Args &&... args) const
Definition def.h:221
P * set(Args &&... args)
Definition def.h:223
friend class World
Definition def.h:910
static constexpr size_t Num_Ops
Definition def.h:907
static constexpr auto Node
Definition def.h:906
const Def * level() const
Definition def.h:903
const Def * op() const
Definition def.h:882
static constexpr auto Node
Definition def.h:886
friend class World
Definition def.h:890
level_t offset() const
Definition def.h:883
static constexpr size_t Num_Ops
Definition def.h:887
@ Type
Definition def.h:864
@ Univ
Definition def.h:864
@ Term
Definition def.h:864
@ Kind
Definition def.h:864
static constexpr auto Node
Definition def.h:861
friend class World
Definition def.h:869
static constexpr size_t Num_Ops
Definition def.h:862
static constexpr size_t Num_Ops
Definition def.h:849
static constexpr auto Node
Definition def.h:848
friend class World
Definition def.h:855
A variable introduced by a binder (mutable).
Definition def.h:825
const Def * type() const
Definition def.h:836
static constexpr auto Node
Definition def.h:838
friend class World
Definition def.h:842
Def * binder() const
The binder of this Var.
Definition def.h:835
static constexpr size_t Num_Ops
Definition def.h:839
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:40
#define MIM_NODE(X)
Definition def.h:26
#define MIM_PROJ(NAME, CONST)
Use as mixin to wrap all kind of Def::proj and Def::projs variants.
Definition def.h:175
Definition ast.h:16
u64 nat_t
Definition types.h:37
DefMap< const Def * > Def2Def
Definition def.h:90
Dep
Tracks whether a Def transitively depends - through its Def::deps() but only up to (and excluding) th...
Definition def.h:133
@ None
Depends on nothing of interest.
Definition def.h:134
bool follow_types
Follow Def::type() dependencies.
Definition def.h:232
u64 flags_t
Definition types.h:39
bool inline_consts
Wire up literals, axioms, etc. with normal edges instead of detaching them.
Definition def.h:233
fe::View< const Def * > Defs
Definition def.h:91
u8 node_t
Definition types.h:38
int max
Maximum recursion depth.
Definition def.h:230
absl::flat_hash_map< K, V, GIDHash< K > > GIDMap
Definition gid.h:24
GIDMap< const Var *, To > VarMap
Definition def.h:110
bool all_annexes
Include all annexes - even if unused (World::dot only).
Definition def.h:231
u64 level_t
Definition types.h:36
GIDMap< const Def *, To > DefMap
Definition def.h:88
GIDSet< Def * > MutSet
Definition def.h:101
bool show_hidden
Render otherwise-transparent detached edges (Var→binder back-edges, shared literals/axioms,...
Definition def.h:235
MutMap< Def * > Mut2Mut
Definition def.h:102
fe::Vector< const Def * > DefVec
Definition def.h:93
GIDSet< const Def * > DefSet
Definition def.h:89
fe::Patricia< const Var, DefKey >::Set Vars
Definition def.h:112
static constexpr size_t Num_Nodes
Definition def.h:127
const Def *(*)(const Def *, const Def *, const Def *) NormalizeFn
Definition def.h:115
uint32_t u32
Definition types.h:27
Mut
Classifies whether a Node may occur as a mutable, an immutable, or both.
Definition def.h:154
@ Imm
Node may be immutable.
Definition def.h:157
fe::Patricia< Def, DefKey >::Set Muts
Definition def.h:103
Judge
Judgement.
Definition def.h:142
@ Intro
Term Introduction like λ(x: Nat): Nat = x.
Definition def.h:145
@ Meta
Meta rules for Universe and Type levels.
Definition def.h:147
@ Form
Type Formation like T -> T.
Definition def.h:144
@ Elim
Term Elimination like f a.
Definition def.h:146
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
bool default_filter
Show Lam::filter() even if it has its default value.
Definition def.h:234
absl::flat_hash_set< K, GIDHash< K > > GIDSet
Definition gid.h:25
uint8_t u8
Definition types.h:27
Node
Definition def.h:120
@ Nat
Definition def.h:122
@ Univ
Definition def.h:122
@ Idx
Definition def.h:122
@ UInc
Definition def.h:122
@ Global
Definition def.h:122
@ Var
Definition def.h:122
@ Axm
Definition def.h:122
@ Type
Definition def.h:122
@ Lit
Definition def.h:122
@ Proxy
Definition def.h:122
@ UMax
Definition def.h:122
GIDMap< Def *, To > MutMap
Definition def.h:100
VarMap< const Var * > Var2Var
Definition def.h:111
Options for Def::dot and World::dot.
Definition def.h:229
Grants fe::Patricia access to Def::gid_.
Definition def.h:79
static std::ostream & stream(std::ostream &, const Def *)
Definition def.cpp:22
static u32 key(const Def *) noexcept
Definition def.h:801
#define CODE(name,...)
Definition tok.h:51