21 X(Lit, Judge::Intro) \
22 X(Axm, Judge::Intro) \
23 X(Var, Judge::Intro) \
24 X(Global, Judge::Intro) \
25 X(Proxy, Judge::Intro) \
26 X(Hole, Judge::Hole ) \
27 X(Type, Judge::Meta ) X(Univ, Judge::Meta ) X(UMax, Judge::Meta) X(UInc, (Judge::Meta )) \
28 X(Pi, Judge::Form ) X(Lam, Judge::Intro) X(App, Judge::Elim) \
29 X(Sigma, Judge::Form ) X(Tuple, Judge::Intro) X(Extract, Judge::Elim) X(Insert, (Judge::Intro | Judge::Elim)) \
30 X(Arr, Judge::Form ) X(Pack, Judge::Intro) \
31 X(Join, Judge::Form ) X(Inj, Judge::Intro) X(Match, Judge::Elim) X(Top, (Judge::Intro )) \
32 X(Meet, Judge::Form ) X(Merge, Judge::Intro) X(Split, Judge::Elim) X(Bot, (Judge::Intro )) \
33 X(Reform, Judge::Form ) X(Rule, Judge::Intro) \
34 X(Uniq, Judge::Form ) \
35 X(Nat, Judge::Form ) \
38#define MIM_IMM_NODE(X) \
43 X(Type) X(Univ) X(UMax) X(UInc) \
45 X(Sigma) X(Tuple) X(Extract) X(Insert) \
47 X(Join) X(Inj) X(Match) X(Top) \
48 X(Meet) X(Merge) X(Split) X(Bot) \
54#define MIM_MUT_NODE(X) \
108#define CODE(node, _) node,
113#define CODE(node, _) +size_t(1)
120enum class Dep :
unsigned {
153template<>
struct fe::is_bit_enum<
mim::
Dep> : std::true_type {};
154template<>
struct fe::is_bit_enum<
mim::
Judge> : std::true_type {};
155template<>
struct fe::is_bit_enum<
mim::
Mut> : std::true_type {};
162#define MIM_PROJ(NAME, CONST) \
163 nat_t num_##NAME##s() CONST noexcept { return ((const Def*)NAME())->num_projs(); } \
164 nat_t num_t##NAME##s() CONST noexcept { return ((const Def*)NAME())->num_tprojs(); } \
165 const Def* NAME(nat_t a, nat_t i) CONST noexcept { return ((const Def*)NAME())->proj(a, i); } \
166 const Def* NAME(nat_t i) CONST noexcept { return ((const Def*)NAME())->proj(i); } \
167 const Def* t##NAME(nat_t i) CONST noexcept { return ((const Def*)NAME())->tproj(i); } \
168 template<nat_t A = std::dynamic_extent, class F> \
169 auto NAME##s(F f) CONST noexcept { \
170 return ((const Def*)NAME())->projs<A, F>(f); \
173 auto t##NAME##s(F f) CONST noexcept { \
174 return ((const Def*)NAME())->tprojs<F>(f); \
176 template<nat_t A = std::dynamic_extent> \
177 auto NAME##s() CONST noexcept { \
178 return ((const Def*)NAME())->projs<A>(); \
180 auto t##NAME##s() CONST noexcept { return ((const Def*)NAME())->tprojs(); } \
182 auto NAME##s(nat_t a, F f) CONST noexcept { \
183 return ((const Def*)NAME())->projs<F>(a, f); \
185 auto NAME##s(nat_t a) CONST noexcept { return ((const Def*)NAME())->projs(a); }
188template<
class P,
class D = Def>
191 __declspec(empty_bases)
195 P* super() {
return static_cast<P*
>(
this); }
196 const P* super()
const {
return static_cast<const P*
>(
this); }
200 template<
bool Ow = false>
const P*
set(Loc l )
const { super()->D::template
set<Ow>(l);
return super(); }
201 template<
bool Ow = false> P*
set(Loc l ) { super()->D::template
set<Ow>(l);
return super(); }
202 template<
bool Ow = false>
const P*
set( Sym s )
const { super()->D::template
set<Ow>(s);
return super(); }
203 template<
bool Ow = false> P*
set( Sym s ) { super()->D::template
set<Ow>(s);
return super(); }
204 template<
bool Ow = false>
const P*
set( std::string s)
const { super()->D::template
set<Ow>(std::move(s));
return super(); }
205 template<
bool Ow = false> P*
set( std::string s) { super()->D::template
set<Ow>(std::move(s));
return super(); }
206 template<
bool Ow = false>
const P*
set(Loc l, Sym s )
const { super()->D::template
set<Ow>(l, s);
return super(); }
207 template<
bool Ow = false> P*
set(Loc l, Sym s ) { super()->D::template
set<Ow>(l, s);
return super(); }
208 template<
bool Ow = false>
const P*
set(Loc l, std::string s)
const { super()->D::template
set<Ow>(l, std::move(s));
return super(); }
209 template<
bool Ow = false> P*
set(Loc l, std::string s) { super()->D::template
set<Ow>(l, std::move(s));
return super(); }
210 template<
bool Ow = false>
const P*
set(
Dbg d )
const { super()->D::template
set<Ow>(d);
return super(); }
211 template<
bool Ow = false> P*
set(
Dbg d ) { super()->D::template
set<Ow>(d);
return super(); }
218 int max = std::numeric_limits<int>::max();
261class Def :
public fe::RuntimeCast<Def> {
263 Def& operator=(
const Def&) =
delete;
264 Def(
const Def&) =
delete;
273 Def(
Node, Def* binder);
282 constexpr u32 gid() const noexcept {
return gid_; }
283 constexpr u32 tid() const noexcept {
return tid_; }
284 constexpr u32 mark() const noexcept {
return mark_; }
285 constexpr size_t hash() const noexcept {
return hash_; }
286 constexpr Node node() const noexcept {
return node_; }
307 const Def*
type() const noexcept;
316 template<
size_t N =
std::dynamic_extent>
317 constexpr auto
ops() const noexcept {
320 const Def*
op(
size_t i)
const noexcept {
return ops()[i]; }
321 constexpr size_t num_ops() const noexcept {
return num_ops_; }
355 const Def*
dep(
size_t i) const noexcept {
return deps()[i]; }
370 bool has_dep() const noexcept {
return dep_ != 0; }
401 template<nat_t A = std::dynamic_extent,
class F>
403 using R = std::decay_t<
decltype(f(
this))>;
404 if constexpr (A == std::dynamic_extent) {
407 std::array<R, A> array;
408 for (
nat_t i = 0; i != A; ++i)
409 array[i] = f(
proj(A, i));
421 using R = std::decay_t<
decltype(f(
this))>;
424 template<nat_t A = std::dynamic_extent>
426 return projs<A>([](
const Def* def) {
return def; });
429 return tprojs([](
const Def* def) {
return def; });
432 return projs(a, [](
const Def* def) {
return def; });
454 template<
class D = Def>
457 if (
auto var = mut->has_var())
return {mut,
var};
459 return {
nullptr,
nullptr};
526 template<
class T = Def,
bool invert = false>
528 if constexpr (std::is_same<T, Def>::value)
529 return mut_ ^ invert ?
const_cast<Def*
>(
this) :
nullptr;
531 return mut_ ^ invert ?
const_cast<Def*
>(
this)->
template isa<T>() :
nullptr;
535 template<
class T = Def,
bool invert = false>
537 assert(mut_ ^ invert);
538 if constexpr (std::is_same<T, Def>::value)
539 return const_cast<Def*
>(
this);
541 return const_cast<Def*
>(
this)->
template as<T>();
547 template<
class T =
Def,
class... Args>
548 T*
expect_mut(std::format_string<Args...> fmt, Args&&... args)
const {
550 fe::throwf(
"expected {}, but got '{}'", std::format(fmt, std::forward<Args>(args)...),
this);
566 template<
bool Ow = false>
const Def*
set(Loc l)
const {
if (Ow || !
dbg_.loc())
dbg_.set(l);
return this; }
567 template<
bool Ow = false> Def*
set(Loc l) {
if (Ow || !
dbg_.loc())
dbg_.set(l);
return this; }
568 template<
bool Ow = false>
const Def*
set(Sym s)
const {
if (Ow || !
dbg_.sym())
dbg_.set(s);
return this; }
569 template<
bool Ow = false> Def*
set(Sym s) {
if (Ow || !
dbg_.sym())
dbg_.set(s);
return this; }
570 template<
bool Ow = false>
const Def*
set( std::string s)
const {
set<Ow>(
sym(std::move(s)));
return this; }
571 template<
bool Ow = false> Def*
set( std::string s) {
set<Ow>(
sym(std::move(s)));
return this; }
572 template<
bool Ow = false>
const Def*
set(Loc l, Sym s )
const {
set<Ow>(l);
set<Ow>(s);
return this; }
574 template<
bool Ow = false>
const Def*
set(Loc l, std::string s)
const {
set<Ow>(l);
set<Ow>(
sym(std::move(s)));
return this; }
575 template<
bool Ow = false> Def*
set(Loc l, std::string s) {
set<Ow>(l);
set<Ow>(
sym(std::move(s)));
return this; }
576 template<
bool Ow = false>
const Def*
set(
Dbg d)
const {
set<Ow>(d.loc(), d.sym());
return this; }
577 template<
bool Ow = false> Def*
set(
Dbg d) {
set<Ow>(d.loc(), d.sym());
return this; }
589 const Def*
debug_suffix(std::string)
const {
return this; }
613 template<
size_t N = std::dynamic_extent>
614 constexpr auto reduce(
const Def* arg)
const {
615 return reduce_(arg).span<N>();
629 virtual const Def*
check([[maybe_unused]]
size_t i,
const Def* def) {
return def; }
657 void dump(
int max)
const;
658 void write(
int max)
const;
659 void write(
int max,
const char* file)
const;
660 std::ostream&
stream(std::ostream&,
int max)
const;
672 [[nodiscard]]
static Cmp cmp(
const Def* a,
const Def* b);
673 [[nodiscard]]
static bool less(
const Def* a,
const Def* b);
674 [[nodiscard]]
static bool greater(
const Def* a,
const Def* b);
682 void dot(
const char* file =
nullptr, DotConfig cfg = {})
const;
683 void dot(
const std::string& file,
DotConfig cfg = {})
const {
return dot(file.c_str(), cfg); }
690 Sym
sym(
const char*)
const;
691 Sym
sym(std::string_view)
const;
692 Sym
sym(std::string)
const;
696 Defs reduce_(
const Def* arg)
const;
697 virtual Def*
stub_(
World&,
const Def*) { fe::unreachable(); }
703 const Def** ops_ptr()
const {
704 return reinterpret_cast<const Def**
>(
reinterpret_cast<char*
>(
const_cast<Def*
>(
this + 1)));
706 bool equal(
const Def* other)
const;
710 [[nodiscard]]
static bool cmp_(
const Def* a,
const Def* b);
719 mutable World* world_;
729 mutable bool annex_ : 1;
741 mutable u32 tid_ = 0;
742 mutable const Def* type_;
744 template<
class D,
size_t N>
748 friend std::ostream&
operator<<(std::ostream&,
const Def*);
778class Univ :
public Def,
public Setters<Univ> {
793class UMax :
public Def,
public Setters<UMax> {
797 static constexpr size_t Num_Ops = std::dynamic_extent;
809class UInc :
public Def,
public Setters<UInc> {
832class Type :
public Def,
public Setters<Type> {
834 Type(
const Def*
level)
864 template<
class T = flags_t>
866 static_assert(
sizeof(T) <= 8);
877 template<
class T = nat_t>
878 static std::optional<T>
isa(
const Def* def) {
880 if (
auto lit = def->isa<Lit>())
return lit->get<T>();
883 template<
class T = nat_t>
884 static T
as(
const Def* def) {
885 return def->as<Lit>()->
get<T>();
888 template<
class T =
nat_t,
class... Args>
889 static T
expect(
const Def* def, std::format_string<Args...> fmt, Args&&... args) {
890 if (
auto res =
isa<T>(def))
return *res;
891 fe::throwf(
"expected {}, but got '{}'", std::format(fmt, std::forward<Args>(args)...), def);
933 static const Def*
isa(
const Def* def);
934 static const Def*
as(
const Def* def) {
939 static std::optional<nat_t>
isa_lit(
const Def* def);
942 assert(res.has_value());
958 template<
class... Args>
962 fe::throwf(
"expected {}, but got '{}'", std::format(fmt, std::forward<Args>(args)...),
type);
975class Proxy :
public Def,
public Setters<Proxy> {
988 template<flags_t Tag>
989 static const Proxy*
isa(
const Def* def) {
990 if (
auto proxy = def->isa<Proxy>(); proxy && proxy->tag() == Tag)
return proxy;
995 static constexpr size_t Num_Ops = std::dynamic_extent;
1006class Global :
public Def,
public Setters<Global> {
1050template<class T> requires std::derived_from<T, mim::Def> struct std::formatter< T*> : fe::ostream_formatter {};
1051template<
class T>
requires std::derived_from<T, mim::Def>
struct std::formatter<const T*> : fe::ostream_formatter {};
1052template<>
struct std::formatter<
mim::
Muts> : fe::ostream_formatter {};
1053template<>
struct std::formatter<
mim::
Vars> : fe::ostream_formatter {};
bool is_set() const
Yields true if empty or the last op is set.
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.
virtual const Def * check(size_t i, const Def *def)
Checks whether the ith operand can be set to def.
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
virtual const Def * check()
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
const Def * refine(size_t i, const Def *new_op) const
Def * set_type(const Def *)
Update type.
std::string_view node_name() const
auto projs(nat_t a, F f) const
bool is_intro() const noexcept
constexpr auto ops() const noexcept
Vars local_vars() const
Vars reachable by following immutable deps().
constexpr flags_t flags() const noexcept
virtual Def * stub_(World &, const Def *)
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().
constexpr u32 tid() const noexcept
Trie id - only used in Trie.
auto projs(nat_t a) const
Judge judge() const noexcept
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
bool is_immutabilizable()
virtual const Def * rebuild_(World &w, const Def *type, Defs ops) const =0
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.
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
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...
virtual const Def * immutabilize()
Tries to make an immutable from a mutable.
bool is_form() const noexcept
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
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?
const Def * dep(size_t i) const noexcept
bool is_mutable() const noexcept
bool is_external() const noexcept
const Def * rebuild(World &w, const Def *type, Defs ops) const
Def::rebuilds this Def while using new_op as substitute for its i'th Def::op.
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
Def * set(Loc l, std::string s)
Def * stub(const Def *type)
static Cmp cmp(const Def *a, const Def *b)
virtual constexpr size_t reduction_offset() const noexcept
First Def::op that needs to be dealt with during reduction; e.g.
nat_t num_projs() const
Yields Def::arity(), if it is a Lit, or 1 otherwise.
std::ostream & stream(std::ostream &, int max) const
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.
virtual 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.
Muts users()
Set of mutables where this mutable is locally referenced.
bool is_closed() const
Has no free_vars()?
const Def * rebuild(const Def *type, Defs ops) const
Vars free_vars() const
Global set of free Vars: extends local_vars() by transitively following mutables as well.
Def * stub(World &w, const Def *type)
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 is_annex() const noexcept
constexpr size_t num_ops() const noexcept
void set(const Def *init)
Global * stub(const Def *type)
const Def * alloced_type() const
const Def * rebuild_(World &, const Def *, Defs) const final
static constexpr size_t Num_Ops
Global * stub_(World &, const Def *) final
static constexpr auto Node
This node is a hole in the IR that is inferred by its context later on.
const Def * rebuild_(World &, const Def *, Defs) const final
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
const Def * rebuild_(World &, const Def *, Defs) const final
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
const Def * rebuild_(World &, const Def *, Defs) const final
static constexpr size_t Num_Ops
static constexpr size_t Num_Ops
const Def * rebuild_(World &, const Def *, Defs) const final
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(Sym s) const
const P * set(Loc l, Sym s) const
const P * set(Loc l) const
const P * set(Dbg d) const
const P * set(Loc l, std::string s) const
P * set(Loc l, std::string s)
const P * set(std::string s) const
static constexpr size_t Num_Ops
static constexpr auto Node
const Def * level() const
const Def * rebuild_(World &, const Def *, Defs) const final
static constexpr auto Node
static constexpr size_t Num_Ops
const Def * rebuild_(World &, const Def *, Defs) const final
static constexpr auto Node
const Def * rebuild_(World &, const Def *, Defs) const final
static constexpr size_t Num_Ops
static constexpr size_t Num_Ops
static constexpr auto Node
const Def * rebuild_(World &, const Def *, Defs) const final
A variable introduced by a binder (mutable).
const Def * rebuild_(World &, const Def *, Defs) const final
static constexpr auto Node
Def * binder() const
The binder of this Var.
static constexpr size_t Num_Ops
This is a thin wrapper for absl::InlinedVector<T, N, A> which is a drop-in replacement for std::vecto...
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
Vector< const Def * > DefVec
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.
int max
Maximum recursion depth.
absl::flat_hash_map< K, V, GIDHash< K > > GIDMap
GIDSet< const Var * > VarSet
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,...
GIDSet< const Def * > DefSet
static constexpr size_t Num_Nodes
const Def *(*)(const Def *, const Def *, const Def *) NormalizeFn
Sets< const Var >::Set Vars
Mut
Classifies whether a Node may occur as a mutable, an immutable, or both.
@ Imm
Node may be immutable.
@ 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.
bool default_filter
Show Lam::filter() even if it has its default value.
absl::flat_hash_set< K, GIDHash< K > > GIDSet
constexpr D bitcast_resize(const S &src) noexcept
A bitcast from src of type S to D, supporting different sizes.
Vector(I, I, A=A()) -> Vector< typename std::iterator_traits< I >::value_type, Default_Inlined_Size< typename std::iterator_traits< I >::value_type >, A >
GIDMap< Def *, To > MutMap
VarMap< const Var * > Var2Var
Options for Def::dot and World::dot.