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