14#include <fe/container.h>
16#include <fe/patricia.h>
27 X(Lit, Judge::Intro) \
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 ) \
44#define MIM_IMM_NODE(X) \
49 X(Type) X(Univ) X(UMax) X(UInc) \
51 X(Sigma) X(Tuple) X(Extract) X(Insert) \
53 X(Join) X(Inj) X(Match) X(Top) \
54 X(Meet) X(Merge) X(Split) X(Bot) \
60#define MIM_MUT_NODE(X) \
81 static std::ostream&
stream(std::ostream&,
const Def*);
91using Defs = fe::View<const Def*>;
93using DefVec = fe::Vector<const Def*>;
103using Muts = fe::Patricia<Def, DefKey>::Set;
112using Vars = fe::Patricia<const Var, DefKey>::Set;
121#define CODE(node, _) node,
126#define CODE(node, _) +size_t(1)
133enum class Dep :
unsigned {
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 {};
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); \
186 auto t##NAME##s(F f) CONST noexcept { \
187 return ((const Def*)NAME())->tprojs<F>(f); \
189 template<nat_t A = std::dynamic_extent> \
190 auto NAME##s() CONST noexcept { \
191 return ((const Def*)NAME())->projs<A>(); \
193 auto t##NAME##s() CONST noexcept { return ((const Def*)NAME())->tprojs(); } \
195 auto NAME##s(nat_t a, F f) CONST noexcept { \
196 return ((const Def*)NAME())->projs<F>(a, f); \
198 auto NAME##s(nat_t a) CONST noexcept { return ((const Def*)NAME())->projs(a); }
204template<
class P,
class D = Def>
207 __declspec(empty_bases)
211 P* super() {
return static_cast<P*
>(
this); }
212 const P* super()
const {
return static_cast<const P*
>(
this); }
215 template<
bool Ow,
class... Args>
216 static constexpr bool Fwd =
requires(D* d, Args&&... args) { d->template
set<Ow>(std::forward<Args>(args)...); };
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(); }
230 int max = std::numeric_limits<int>::max();
273class Def :
public fe::RuntimeCast<Def> {
275 Def& operator=(
const Def&) =
delete;
276 Def(
const Def&) =
delete;
285 Def(
Node, Def* binder);
294 constexpr u32 gid() const noexcept {
return gid_; }
295 constexpr u32 mark() const noexcept {
return mark_; }
296 constexpr size_t hash() const noexcept {
return hash_; }
297 constexpr Node node() const noexcept {
return node_; }
303 fe::Error&
error() const noexcept;
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)...);
332 const Def*
type() const noexcept;
339 return t ? t->template isa<T>() :
nullptr;
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_);
351 const Def*
op(
size_t i)
const noexcept {
return ops()[i]; }
352 constexpr size_t num_ops() const noexcept {
return num_ops_; }
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");
393 const Def*
dep(
size_t i) const noexcept {
return deps()[i]; }
408 bool has_dep() const noexcept {
return dep_ != 0; }
439 template<nat_t A = std::dynamic_extent,
class F>
441 using R = std::decay_t<
decltype(f(
this))>;
442 if constexpr (A == std::dynamic_extent) {
445 std::array<R, A> array;
446 for (
nat_t i = 0; i != A; ++i)
447 array[i] = f(
proj(A, i));
459 using R = std::decay_t<
decltype(f(
this))>;
460 return fe::Vector<R>(a, [&](
nat_t i) {
return f(
proj(a, i)); });
462 template<nat_t A = std::dynamic_extent>
464 return projs<A>([](
const Def* def) {
return def; });
467 return tprojs([](
const Def* def) {
return def; });
470 return projs(a, [](
const Def* def) {
return def; });
489 template<
class D = Def>
494 return {
nullptr,
nullptr};
579 template<
class T = Def,
bool invert = false>
581 if constexpr (std::is_same_v<T, Def>)
582 return mut_ ^ invert ?
const_cast<Def*
>(
this) :
nullptr;
584 return mut_ ^ invert ?
const_cast<Def*
>(
this)->
template isa<T>() :
nullptr;
588 template<
class T = Def,
bool invert = false>
590 assert(mut_ ^ invert);
591 if constexpr (std::is_same_v<T, Def>)
592 return const_cast<Def*
>(
this);
594 return const_cast<Def*
>(
this)->
template as<T>();
600 template<
class T =
Def,
class... Args>
601 T*
expect_mut(std::format_string<Args...> fmt, Args&&... args)
const {
603 fe::throwf(
"expected {}, but got `{}`", std::format(fmt, std::forward<Args>(args)...),
this);
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; }
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; }
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; }
647 const Def*
debug_suffix(std::string)
const {
return this; }
659 template<
size_t N = std::dynamic_extent>
660 constexpr auto reduce(
const Def* arg)
const {
661 return reduce_(arg).span<N>();
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;
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);
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); }
736 Sym
sym(
const char*)
const;
737 Sym
sym(std::string_view)
const;
738 Sym
sym(std::string)
const;
745 Defs reduce_(
const Def* arg)
const;
753 const Def** ops_ptr()
const {
754 return reinterpret_cast<const Def**
>(
reinterpret_cast<char*
>(
const_cast<Def*
>(
this + 1)));
756 bool equal(
const Def* other)
const;
760 [[nodiscard]]
static bool cmp_(
const Def* a,
const Def* b);
768 mutable World* world_;
778 mutable bool annex_ : 1;
793 mutable const Def* type_;
798 friend std::ostream&
operator<<(std::ostream&,
const Def*);
807static_assert(!std::is_polymorphic_v<Def>,
"Def must not have a vtable; dispatch on Def::node() instead");
813#define CODE(node, judge) judge,
817 return Judges[
node_t(node_)];
845class Univ :
public Def,
public Setters<Univ> {
858class UMax :
public Def,
public Setters<UMax> {
862 static constexpr size_t Num_Ops = std::dynamic_extent;
872class UInc :
public Def,
public Setters<UInc> {
893class Type :
public Def,
public Setters<Type> {
895 Type(
const Def*
level)
923 template<
class T = flags_t>
925 static_assert(
sizeof(T) <= 8);
926 return fe::bitcast_resize<T>(
flags_);
936 template<
class T = nat_t>
937 static std::optional<T>
isa(
const Def* def) {
939 if (
auto lit = def->isa<Lit>())
return lit->get<T>();
942 template<
class T = nat_t>
943 static T
as(
const Def* def) {
944 return def->as<Lit>()->
get<T>();
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);
988 static const Def*
isa(
const Def* def);
989 static const Def*
as(
const Def* def) {
994 static std::optional<nat_t>
isa_lit(
const Def* def);
997 assert(res.has_value());
1013 template<
class... Args>
1017 fe::throwf(
"expected {}, but got `{}`", std::format(fmt, std::forward<Args>(args)...),
type);
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;
1052 static constexpr size_t Num_Ops = std::dynamic_extent;
1061class Global :
public Def,
public Setters<Global> {
1100 for (
auto def =
this;;) {
1101 switch (def->node_) {
1104 case Node::Var: def = def->binder_;
break;
1105 default: def = def->type_;
break;
1116inline bool Def::equal(
const Def* other)
const {
1118 if (mut_ || other->mut_ || node_ ==
Node::Univ)
return this == other;
1121 if (
auto var = isa<Var>())
return other->isa<
Var>() &&
var->binder() == other->as<
Var>()->binder();
1123 bool result = this->
node() == other->
node() && this->flags() == other->
flags()
1124 && this->num_ops() == other->
num_ops() && this->type() == other->
type();
1126 for (
size_t i = 0, e =
num_ops(); result && i != e; ++i)
1127 result &= this->
op(i) == other->
op(i);
1137 return fe::StreamFn{[def](std::ostream& os) -> std::ostream& {
1139 return os <<
"<no type>";
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 {};
Loc err_loc() const
Returns a blame Loc from World::get_loc, this Def, or its nearest located dependency,...
void set_dbg(Dbg) const
Interns dbg via Driver::dbg and stores the key in Def::dbg_.
size_t num_deps() const noexcept
const Def * zonk_mut() const
If mutable, zonk()s all ops and tries to immutabilize it; otherwise just zonk.
const Def * set(Dbg d) const
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.
constexpr Node node() const noexcept
Def * set(size_t i, const Def *)
Successively set from left to right.
T * as_mut() const
Asserts that this is a mutable, casts constness away and performs a static_cast to T.
const Var * has_var() const
As above if this is a mutable.
void dirty(bool dirty=true) noexcept
bool has_dep() const noexcept
Defs deps() const noexcept
bool is_elim() const noexcept
nat_t num_tprojs() const
As above but yields 1, if Flags::scalarize_threshold is exceeded.
const Def * zonk() const
If Holes have been filled, reconstruct the program without them.
World & world() const noexcept
Def * set_type(const Def *)
Update type.
std::string_view node_name() const
auto projs(nat_t a, F f) const
fe::Error & error() const noexcept
bool is_intro() const noexcept
constexpr auto ops() const noexcept
Vars local_vars() const
Vars reachable by following immutable deps().
size_t reduction_offset() const noexcept
First Def::op that needs to be dealt with during reduction; e.g.
constexpr flags_t flags() const noexcept
bool has_dep(Dep d) const noexcept
const Def * set(std::string s) const
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
constexpr u32 mark() const noexcept
Used internally by free_vars().
auto projs(nat_t a) const
Judge judge() const noexcept
Def must never become polymorphic: a vptr costs 8 bytes on every node in the World,...
friend std::ostream & operator<<(std::ostream &, const Def *)
This will stream def as an operand.
bool is_term() const
Is this Def a term, i.e. is its type() a Type?
const Def * debug_prefix(std::string) const
const Def * op(size_t i) const noexcept
void dot(std::ostream &os, DotConfig cfg={}) const
DbgKey dbg_key() const
Cheap handle for other->set(this->dbg_key()).
bool is_immutabilizable()
std::pair< D *, const Var * > isa_binder() const
Is this a mutable that introduces a Var?
const Def * var(nat_t a, nat_t i) noexcept
void transfer_external(Def *to)
const Def * unfold_type() const
Yields the type of this Def and builds a new Type (UInc n) if necessary.
const Def * proj(nat_t i) const
As above but takes Def::num_projs as arity.
bool has_free_vars() const
Same as !free_vars().empty().
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
bool is_open() const
Same as has_free_vars().
constexpr size_t hash() const noexcept
bool is_dirty() const noexcept
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...
bool has_free_var(const Var *) const
Same as free_vars().contains(var).
bool is_form() const noexcept
void set_dbg_(Dbg, bool ow) const
Backs Def::set(Dbg).
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
bool is_ground() const
Immutable that contains neither mutables nor Vars.
const Def * debug_suffix(std::string) const
const Def * set(Sym s) const
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Def * outermost_binder() const
Transitively walks up free_vars() till the outermoust binder has been found.
bool nests(Def *mut)
Does this nest mut?
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.
const Def * dep(size_t i) const noexcept
bool is_mutable() const noexcept
bool is_external() const noexcept
const Def * set(Loc l, Sym s) const
static bool less(const Def *a, const Def *b)
bool is_meta() const noexcept
static bool greater(const Def *a, const Def *b)
void dot(const std::string &file, DotConfig cfg={}) const
const Def * set(Loc l) const
void write(int max) const
const Def * var()
Not necessarily a Var: E.g., if the return type is [], this will yield ().
Def * set(Loc l, std::string s)
static Cmp cmp(const Def *a, const Def *b)
Driver & driver() const noexcept
nat_t num_projs() const
Yields Def::arity(), if it is a Lit, or 1 otherwise.
std::ostream & stream(std::ostream &, int max) const
const Def * immutabilize()
friend void swap(World &, World &) noexcept
const Def * var_type()
If this is a binder, compute the type of its Variable.
constexpr u32 gid() const noexcept
Global id - unique number for this Def.
const Def * arity() const
Number of elements available to Extract / Insert (may be dynamic).
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
std::string unique_name() const
name + "_" + Def::gid
constexpr auto reduce(const Def *arg) const
const Def * set(Loc l, std::string s) const
const T * isa_imm() const
bool needs_zonk() const
Yields true, if Def::local_muts() contain a Hole that is set.
void set_dbg_key_(DbgKey, bool ow) const
Backs Def::set(DbgKey).
Muts users()
Set of mutables where this mutable is locally referenced.
bool is_closed() const
Same as !has_free_vars().
Vars free_vars() const
Global set of free Vars: extends local_vars() by transitively following mutables as well.
const Def * set(DbgKey key) const
Adopts the Dbg behind key - just copies the interned index, so nothing is re-interned.
Dbg dbg() const
Looks up Def::dbg_ in Driver::dbg.
const Def * tproj(nat_t i) const
As above but takes Def::num_tprojs.
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
bool has_free_vars_in(Vars) const
Same as vars.has_intersection(free_vars()).
bool is_annex() const noexcept
const T * isa_type() const
Is Def::unfold_type a T? Yields nullptr for Univ, which has no type at all.
constexpr size_t num_ops() const noexcept
const Def * check()
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Some "global" variables needed all over the place.
void set(const Def *init)
const Def * alloced_type() const
static constexpr size_t Num_Ops
static constexpr auto Node
This node is a hole in the IR that is inferred by its context later on.
static constexpr auto Node
static nat_t as_lit(const Def *def)
static constexpr nat_t size2bitwidth(nat_t n)
static constexpr nat_t bitwidth2size(nat_t n)
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
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...
static std::optional< nat_t > isa_lit(const Def *def)
static constexpr size_t Num_Ops
static const Def * as(const Def *def)
static constexpr auto Node
static std::optional< T > isa(const Def *def)
static T as(const Def *def)
static constexpr size_t Num_Ops
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.
static constexpr auto Node
static constexpr size_t Num_Ops
Used as intermediate value during optimizatinos such as Analysis.
static constexpr size_t Num_Ops
static constexpr auto Node
static const Proxy * isa(const Def *def)
CRTP-based mixin to declare setters for Def::loc & Def::name using a covariant return type.
const P * set(Args &&... args) const
static constexpr size_t Num_Ops
static constexpr auto Node
const Def * level() const
static constexpr auto Node
static constexpr size_t Num_Ops
static constexpr auto Node
static constexpr size_t Num_Ops
static constexpr size_t Num_Ops
static constexpr auto Node
A variable introduced by a binder (mutable).
static constexpr auto Node
Def * binder() const
The binder of this Var.
static constexpr size_t Num_Ops
The World represents the whole program and manages creation of MimIR nodes (Defs).
#define MIM_PROJ(NAME, CONST)
Use as mixin to wrap all kind of Def::proj and Def::projs variants.
DefMap< const Def * > Def2Def
Dep
Tracks whether a Def transitively depends - through its Def::deps() but only up to (and excluding) th...
@ None
Depends on nothing of interest.
bool follow_types
Follow Def::type() dependencies.
bool inline_consts
Wire up literals, axioms, etc. with normal edges instead of detaching them.
fe::View< const Def * > Defs
int max
Maximum recursion depth.
absl::flat_hash_map< K, V, GIDHash< K > > GIDMap
GIDMap< const Var *, To > VarMap
bool all_annexes
Include all annexes - even if unused (World::dot only).
GIDMap< const Def *, To > DefMap
bool show_hidden
Render otherwise-transparent detached edges (Var→binder back-edges, shared literals/axioms,...
fe::Vector< const Def * > DefVec
GIDSet< const Def * > DefSet
fe::Patricia< const Var, DefKey >::Set Vars
static constexpr size_t Num_Nodes
const Def *(*)(const Def *, const Def *, const Def *) NormalizeFn
Mut
Classifies whether a Node may occur as a mutable, an immutable, or both.
@ Imm
Node may be immutable.
fe::Patricia< Def, DefKey >::Set Muts
@ Intro
Term Introduction like λ(x: Nat): Nat = x.
@ Meta
Meta rules for Universe and Type levels.
@ Form
Type Formation like T -> T.
@ Elim
Term Elimination like f a.
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.
bool default_filter
Show Lam::filter() even if it has its default value.
absl::flat_hash_set< K, GIDHash< K > > GIDSet
GIDMap< Def *, To > MutMap
VarMap< const Var * > Var2Var
Options for Def::dot and World::dot.
Grants fe::Patricia access to Def::gid_.
static std::ostream & stream(std::ostream &, const Def *)
static u32 key(const Def *) noexcept