MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
lam.h
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <variant>
5
6#include "mim/def.h"
7
8namespace mim {
9
10class Extract;
11
12/// A [dependent function type](https://en.wikipedia.org/wiki/Dependent_type#%CE%A0_type).
13/// @see Lam
14class Pi : public Def, public Setters<Pi> {
15protected:
16 /// Constructor for an *immutable* Pi.
17 Pi(const Def* type, const Def* dom, const Def* codom, bool implicit)
18 : Def(Node, type, {dom, codom}, (flags_t)implicit) {}
19 /// Constructor for a *mutable* Pi.
20 Pi(const Def* type, bool implicit)
21 : Def(Node, type, 2, implicit ? 1 : 0) {}
22
23public:
24 /// @name Get/Set implicit
25 ///@{
26 bool is_implicit() const { return flags(); }
27 /// Is @p d an Pi::is_implicit Pi?
28 static const Pi* isa_implicit(const Def* d) {
29 if (auto pi = d->isa<Pi>(); pi && pi->is_implicit()) return pi;
30 return nullptr;
31 }
32 ///@}
33
34 /// @name dom & codom
35 /// @anchor pi_dom
36 /// @see @ref proj
37 ///@{
38 const Def* dom() const { return op(0); }
39 const Def* codom() const { return op(1); }
40 MIM_PROJ(dom, const)
42 ///@}
43
44 /// @name Continuations
45 /// @anchor continuations
46 /// A *continuation* is a Pi whose Pi::codom is mim::Bot%tom.
47 /// It is *returning*, if it has a Pi::ret_pi, and a *basic block* otherwise.
48 ///@{
49 // clang-format off
50 static const Pi* isa_cn (const Def* d) { auto pi = d->isa<Pi>(); return pi && pi->codom()->node() == Node::Bot ? pi : nullptr; }
51 static const Pi* isa_returning (const Def* d) { auto pi = isa_cn(d); return pi && pi->ret_pi() ? pi : nullptr; }
52 static const Pi* isa_basicblock(const Def* d) { auto pi = isa_cn(d); return pi && !pi->ret_pi() ? pi : nullptr; }
53 // clang-format on
54
55 /// Yields the last Pi::dom, if Pi::isa_basicblock.
56 const Pi* ret_pi() const;
57 /// Pi::dom%ain of Pi::ret_pi.
58 const Def* ret_dom() const { return ret_pi()->dom(); }
59 ///@}
60
61 /// @name Setters
62 /// @see @ref set_ops "Setting Ops"
63 ///@{
64 using Setters<Pi>::set;
65 Pi* set(const Def* dom, const Def* codom) { return Def::set({dom, codom})->as<Pi>(); }
66 Pi* set_dom(const Def* dom) { return Def::set(0, dom)->as<Pi>(); }
68 Pi* set_codom(const Def* codom) { return Def::set(1, codom)->as<Pi>(); }
69 Pi* unset() { return Def::unset()->as<Pi>(); }
70 ///@}
71
72 /// @name Type Checking
73 ///@{
74 static const Def* infer(const Def* dom, const Def* codom);
75 ///@}
76
77 /// @name Reduce
78 ///@{
79 const Def* reduce(const Def* arg) const { return Def::reduce(arg).front(); }
80 ///@}
81
82 static constexpr auto Node = mim::Node::Pi;
83 static constexpr size_t Num_Ops = 2;
84
85private:
86 friend class World;
87};
88
89/// A function.
90/// @see Pi
91class Lam : public Def, public Setters<Lam> {
92private:
93 Lam(const Pi* pi, const Def* filter, const Def* body)
94 : Def(Node, pi, {filter, body}, 0) {}
95 Lam(const Pi* pi)
96 : Def(Node, pi, 2, 0) {}
97
98public:
99 using Filter = std::variant<bool, const Def*>;
100
101 /// @name ops
102 ///@{
103 const Def* filter() const { return op(0); }
104 const Def* body() const { return op(1); }
105 Lam* forwardee() const; ///< `λ x = f arg` where `f` is a mut Lam that is_set();
106 ///@}
107
108 /// @name type
109 /// @anchor lam_dom
110 /// @see @ref proj
111 ///@{
112 const Pi* type() const { return Def::type()->as<Pi>(); }
113 const Def* dom() const { return type()->dom(); }
114 const Def* codom() const { return type()->codom(); }
115 /// Does this type() itself or dom() has a Var?
116 bool is_dependent() const { return type()->has_var() || dom()->has_var(); }
117 MIM_PROJ(dom, const)
119 ///@}
120
121 /// @name Continuations
122 /// @see @ref continuations "Pi: Continuations"
123 ///@{
124 // clang-format off
125 static const Lam* isa_cn (const Def* d) { auto lam = d->isa <Lam>(); return lam && Pi::isa_cn (lam->type()) ? lam : nullptr; }
126 static const Lam* isa_returning (const Def* d) { auto lam = d->isa <Lam>(); return lam && Pi::isa_returning (lam->type()) ? lam : nullptr; }
127 static const Lam* isa_basicblock (const Def* d) { auto lam = d->isa <Lam>(); return lam && Pi::isa_basicblock(lam->type()) ? lam : nullptr; }
128 static Lam* isa_mut_cn (const Def* d) { auto lam = d->isa_mut<Lam>(); return lam && Pi::isa_cn (lam->type()) ? lam : nullptr; }
129 static Lam* isa_mut_returning (const Def* d) { auto lam = d->isa_mut<Lam>(); return lam && Pi::isa_returning (lam->type()) ? lam : nullptr; }
130 static Lam* isa_mut_basicblock(const Def* d) { auto lam = d->isa_mut<Lam>(); return lam && Pi::isa_basicblock(lam->type()) ? lam : nullptr; }
131 // clang-format on
132
133 const Pi* ret_pi() const { return type()->ret_pi(); }
134 const Def* ret_dom() const { return ret_pi()->dom(); }
135 /// Yields the Lam::var of the Lam::ret_pi.
136 const Def* ret_var() {
137 if (!ret_pi()) return nullptr;
138 auto n = num_vars(); // compute the arity once and hand it to the (a, i) projection
139 return var(n, n - 1);
140 }
141 /// Yields the `y` of `λ (x, ret) = ret y` - the argument @p d's body hands to its Lam::ret_var.
142 /// `nullptr` if @p d is not a set, mutable Lam with such a body.
143 static const Def* isa_ret_arg(const Def* d);
144 ///@}
145
146 /// @name Setters
147 /// Lam::Filter is a `std::variant<bool, const Def*>` that lets you set the Lam::filter() like this:
148 /// ```cpp
149 /// lam1->app(true, f, arg);
150 /// lam2->app(my_filter_def, f, arg);
151 /// ```
152 /// @note The filter belongs to the *Lam%bda* and **not** the body.
153 /// @see @ref set_ops "Setting Ops"
154 ///@{
155 using Setters<Lam>::set;
156 Lam* set(Filter filter, const Def* body);
157 Lam* set_filter(Filter); ///< Set filter first.
158 Lam* set_body(const Def* body) { return Def::set(1, body)->as<Lam>(); } ///< Set body second.
159 /// Set body to an App of @p callee and @p arg.
160 Lam* app(Filter filter, const Def* callee, const Def* arg);
161 /// Set body to an App of @p callee and @p args.
162 Lam* app(Filter filter, const Def* callee, Defs args);
163 /// Set body to an App of `(f, t)#cond mem` or `(f, t)#cond ()` if @p mem is `nullptr`.
164 Lam* branch(Filter filter, const Def* cond, const Def* t, const Def* f, const Def* arg = nullptr);
165 Lam* set(Defs ops) { return Def::set(ops)->as<Lam>(); }
166 Lam* unset() { return Def::unset()->as<Lam>(); }
167 ///@}
168
169 /// @name Reduce
170 ///@{
171 using Def::reduce;
172 Defs reduce(Defs) const;
173 const Def* reduce_body(const Def* arg) const { return reduce(arg).back(); }
174 ///@}
175
176 /// @name Eta-Conversion
177 ///@{
178 static Lam* eta_expand(Filter, const Def* f);
179 static Lam* eta_expand(const Def* f) { return eta_expand(true, f); } ///< Use `true` Filter.
180 /// Fast-forwards @p def along its chain of η-redexes `λ x = f x` where `x` ∉ `FV(f)`.
181 /// Yields the end of the chain or `nullptr` if @p def is no η-redex.
182 /// A chain running into an η-cycle like `ping = λn.pong n; pong = λn.ping n` ends at the cycle's entry.
183 /// Hence, a Lam on such a cycle is no η-redex.
184 /// Neither is a Lam whose reduct mentions an η-redex, like `b = λx.(e, b)#c x`: reducing might unfold a recursion.
185 static const Def* eta_reduce(const Def* def);
186 ///@}
187
188 /// @name Rewritable
189 ///@{
190 /// Is `this` neither is_external() nor is_annex() but is_set()?
191 bool is_rewritable() { return !is_external() && !is_annex() && is_set(); }
192 /// Yields @p def as a mutable Lam, if it is_rewritable() and `nullptr` otherwise.
193 static Lam* isa_rewritable(const Def* def) {
194 if (auto lam = def->isa_mut<Lam>(); lam && lam->is_rewritable()) return lam;
195 return nullptr;
196 }
197 ///@}
198
199 static constexpr auto Node = mim::Node::Lam;
200 static constexpr size_t Num_Ops = 2;
201
202private:
203 friend class World;
204};
205
206/// @name Lam
207/// GIDSet / GIDMap keyed by Lam::gid of `Lam*`.
208///@{
209template<class To>
213///@}
214
215class App : public Def, public Setters<App> {
216private:
217 App(const Axm* axm, u8 curry, u8 trip, const Def* type, const Def* callee, const Def* arg)
218 : Def(Node, type, {callee, arg}, 0) {
219 axm_ = axm;
220 curry_ = curry;
221 trip_ = trip;
222 }
223
224 template<size_t N>
225 static auto uncurry_args_(const Def* def) {
226 auto args = std::array<const Def*, N>();
227 for (size_t i = N; i-- != 0;)
228 if (auto app = def->isa<App>()) args[i] = app->arg(), def = app->callee();
229 return args;
230 }
231
232public:
233 using Setters<App>::set;
234
235 /// @name callee
236 ///@{
237 const Def* callee() const { return op(0); }
238 const App* decurry() const { return callee()->as<App>(); } ///< Returns App::callee again as App.
239 const Pi* callee_type() const { return callee()->type()->as<Pi>(); }
240 /// The App::callee of @p def - or `nullptr`, if @p def isn't an App at all.
241 static const Def* callee_of(const Def* def) {
242 auto app = def->isa<App>();
243 return app ? app->callee() : nullptr;
244 }
245 ///@}
246
247 /// @name arg
248 /// @anchor app_arg
249 /// @see @ref proj
250 ///@{
251 const Def* arg() const { return op(1); }
252 MIM_PROJ(arg, const)
253 ///@}
254
255 /// @name Get axm, current curry counter and trip count
256 ///@{
257 const Axm* axm() const { return axm_; }
258 u8 curry() const { return curry_; }
259 u8 trip() const { return trip_; }
260 ///@}
261
262 /// @name Uncurry
263 /// Peel off the @p N innermost App%s of a curried App.
264 /// Use like this:
265 /// ```
266 /// auto [abc, de] = app->uncurry_args<2>();
267 /// auto [a, b, c] = abc->projs<3>();
268 /// auto [d, e] = de->projs<2>();
269 /// auto axm = app->uncurry_callee();
270 /// ```
271 /// If you "overshoot" the number of curried App%s, the superflous args on the left will be `nullptr`.
272 ///@{
273 // clang-format off
274 template<size_t N> static auto uncurry_args(const Def* def) { return uncurry_args_<N>(def ); }
275 template<size_t N> auto uncurry_args() const { return uncurry_args_<N>(this); }
276
277 /// The innermost App::callee - the one that isn't an App itself.
278 static const Def* uncurry_callee(const Def* def) { while (auto app = def->isa<App>()) def = app->callee(); return def; }
279 const Def* uncurry_callee() const { return uncurry_callee(this); }
280 // clang-format on
281 ///@}
282
283 static constexpr auto Node = mim::Node::App;
284 static constexpr size_t Num_Ops = 2;
285
286private:
287 friend class World;
288};
289
290} // namespace mim
const Axm * axm() const
Definition lam.h:257
static constexpr size_t Num_Ops
Definition lam.h:284
static const Def * uncurry_callee(const Def *def)
The innermost App::callee - the one that isn't an App itself.
Definition lam.h:278
const Pi * callee_type() const
Definition lam.h:239
u8 curry() const
Definition lam.h:258
auto args(Projector auto f) const noexcept
Definition lam.h:252
const App * decurry() const
Returns App::callee again as App.
Definition lam.h:238
static constexpr auto Node
Definition lam.h:283
friend class World
Definition lam.h:287
const Def * callee() const
Definition lam.h:237
static auto uncurry_args(const Def *def)
Definition lam.h:274
static const Def * callee_of(const Def *def)
The App::callee of def - or nullptr, if def isn't an App at all.
Definition lam.h:241
const Def * uncurry_callee() const
Definition lam.h:279
auto uncurry_args() const
Definition lam.h:275
const Def * arg() const
Definition lam.h:251
u8 trip() const
Definition lam.h:259
Definition axm.h:9
Base class for all Defs.
Definition def.h:289
bool is_set() const
Definition def.h:396
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
u8 trip_
Definition def.h:805
constexpr auto ops() const noexcept
Definition def.h:374
constexpr flags_t flags() const noexcept
Definition def.h:311
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:588
u8 curry_
Definition def.h:804
const Def * op(size_t i) const noexcept
Definition def.h:377
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:492
nat_t num_vars() noexcept
Definition def.h:492
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1119
bool is_external() const noexcept
Definition def.h:569
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
Definition def.cpp:205
constexpr auto reduce(const Def *arg) const
Definition def.h:681
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
Definition def.h:496
bool is_annex() const noexcept
Definition def.h:573
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:360
A function.
Definition lam.h:91
std::variant< bool, const Def * > Filter
Definition lam.h:99
Lam * unset()
Definition lam.h:166
const Def * filter() const
Definition lam.h:103
static const Lam * isa_cn(const Def *d)
Definition lam.h:125
static constexpr size_t Num_Ops
Definition lam.h:200
Lam * forwardee() const
λ x = f arg where f is a mut Lam that is_set();
Definition lam.cpp:27
Lam * set(Filter filter, const Def *body)
Definition lam.cpp:34
bool is_rewritable()
Definition lam.h:191
static Lam * eta_expand(Filter, const Def *f)
Definition lam.cpp:84
Lam * set_filter(Filter)
Set filter first.
Definition lam.cpp:33
static Lam * isa_mut_basicblock(const Def *d)
Definition lam.h:130
Defs reduce(Defs) const
Definition lam.cpp:44
static const Lam * isa_returning(const Def *d)
Definition lam.h:126
const Pi * ret_pi() const
Definition lam.h:133
const Def * dom() const
Definition lam.h:113
static Lam * isa_mut_returning(const Def *d)
Definition lam.h:129
const Pi * type() const
Definition lam.h:112
Lam * set(Defs ops)
Definition lam.h:165
static constexpr auto Node
Definition lam.h:199
static const Def * eta_reduce(const Def *def)
Fast-forwards def along its chain of η-redexes λ x = f x where x ∉ FV(f).
Definition lam.cpp:76
friend class World
Definition lam.h:203
static const Lam * isa_basicblock(const Def *d)
Definition lam.h:127
Lam * branch(Filter filter, const Def *cond, const Def *t, const Def *f, const Def *arg=nullptr)
Set body to an App of (f, t)#cond mem or (f, t)#cond () if mem is nullptr.
Definition lam.cpp:40
bool is_dependent() const
Does this type() itself or dom() has a Var?
Definition lam.h:116
Lam * app(Filter filter, const Def *callee, const Def *arg)
Set body to an App of callee and arg.
Definition lam.cpp:35
const Def * ret_dom() const
Definition lam.h:134
Lam * set_body(const Def *body)
Set body second.
Definition lam.h:158
const Def * body() const
Definition lam.h:104
static Lam * isa_rewritable(const Def *def)
Yields def as a mutable Lam, if it is_rewritable() and nullptr otherwise.
Definition lam.h:193
static Lam * isa_mut_cn(const Def *d)
Definition lam.h:128
static const Def * isa_ret_arg(const Def *d)
Yields the y of λ (x, ret) = ret y - the argument d's body hands to its Lam::ret_var.
Definition lam.cpp:46
static Lam * eta_expand(const Def *f)
Use true Filter.
Definition lam.h:179
const Def * reduce_body(const Def *arg) const
Definition lam.h:173
const Def * ret_var()
Yields the Lam::var of the Lam::ret_pi.
Definition lam.h:136
const Def * codom() const
Definition lam.h:114
A dependent function type.
Definition lam.h:14
const Def * ret_dom() const
Pi::domain of Pi::ret_pi.
Definition lam.h:58
Pi(const Def *type, bool implicit)
Constructor for a mutable Pi.
Definition lam.h:20
Pi * unset()
Definition lam.h:69
Pi * set_codom(const Def *codom)
Definition lam.h:68
static constexpr size_t Num_Ops
Definition lam.h:83
Pi(const Def *type, const Def *dom, const Def *codom, bool implicit)
Constructor for an immutable Pi.
Definition lam.h:17
static const Pi * isa_cn(const Def *d)
Definition lam.h:50
static const Pi * isa_basicblock(const Def *d)
Definition lam.h:52
static const Def * infer(const Def *dom, const Def *codom)
Definition check.cpp:496
bool is_implicit() const
Definition lam.h:26
const Def * dom() const
Definition lam.h:38
friend class World
Definition lam.h:86
const Def * codom() const
Definition lam.h:39
static const Pi * isa_implicit(const Def *d)
Is d an Pi::is_implicit Pi?
Definition lam.h:28
Pi * set(const Def *dom, const Def *codom)
Definition lam.h:65
auto doms() const noexcept
Definition lam.h:40
static constexpr auto Node
Definition lam.h:82
const Def * reduce(const Def *arg) const
Definition lam.h:79
const Pi * ret_pi() const
Yields the last Pi::dom, if Pi::isa_basicblock.
Definition lam.cpp:13
Pi * set_dom(const Def *dom)
Definition lam.h:66
static const Pi * isa_returning(const Def *d)
Definition lam.h:51
CRTP-based mixin to declare setters for Def::loc & Def::name using a covariant return type.
Definition def.h:220
#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
ankerl::unordered_dense::map< K, V, GIDHash< K > > GIDMap
Definition gid.h:22
GIDSet< Lam * > LamSet
Definition lam.h:211
u64 flags_t
Definition types.h:39
fe::View< const Def * > Defs
Definition def.h:96
LamMap< Lam * > Lam2Lam
Definition lam.h:212
GIDMap< Lam *, To > LamMap
Definition lam.h:210
ankerl::unordered_dense::set< K, GIDHash< K > > GIDSet
Definition gid.h:23
uint8_t u8
Definition types.h:27
@ Pi
Definition def.h:125
@ Bot
Definition def.h:125
@ Lam
Definition def.h:125
@ App
Definition def.h:125