MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
lam.h
Go to the documentation of this file.
1#pragma once
2
3#include <span>
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 Pi* make_implicit() { return flags_ = (flags_t) true, this; }
28 Pi* make_explicit() { return flags_ = (flags_t) false, this; }
29 ///@}
30
31 /// @name dom & codom
32 /// @anchor pi_dom
33 /// @see @ref proj
34 ///@{
35 const Def* dom() const { return op(0); }
36 const Def* codom() const { return op(1); }
37 MIM_PROJ(dom, const)
38 MIM_PROJ(codom, const)
39 ///@}
40
41 /// @name Continuations
42 /// @anchor continuations
43 /// Checks certain properties of @p d regarding continuations.
44 ///@{
45 // clang-format off
46 /// Is this a continuation - i.e. is the Pi::codom mim::Bot%tom?
47 static const Pi* isa_cn(const Def* d) { return d->isa<Pi>() && d->as<Pi>()->codom()->node() == Node::Bot ? d->as<Pi>() : nullptr; }
48 /// Is this a continuation (Pi::isa_cn) which has a Pi::ret_pi?
49 static const Pi* isa_returning(const Def* d) { return isa_cn(d) && d->as<Pi>()->ret_pi() ? d->as<Pi>() : nullptr; }
50 /// Is this a continuation (Pi::isa_cn) that is **not** Pi::isa_returning?
51 static const Pi* isa_basicblock(const Def* d) { return isa_cn(d) && !d->as<Pi>()->ret_pi() ? d->as<Pi>() : nullptr; }
52 // clang-format on
53 /// Is @p d an Pi::is_implicit (mutable) Pi?
54 static Pi* isa_implicit(const Def* d) {
55 if (auto pi = d->isa_mut<Pi>(); pi && pi->is_implicit()) return pi;
56 return nullptr;
57 }
58 /// Yields the Pi::ret_pi() of @p d, if it is in fact a Pi.
59 static const Pi* has_ret_pi(const Def* d) { return d->isa<Pi>() ? d->as<Pi>()->ret_pi() : nullptr; }
60 ///@}
61
62 /// @name Return Continuation
63 /// @anchor return_continuation
64 ///@{
65
66 /// Yields the last Pi::dom, if Pi::isa_basicblock.
67 const Pi* ret_pi() const;
68 /// Pi::dom%ain of Pi::ret_pi.
69 const Def* ret_dom() const { return ret_pi()->dom(); }
70 ///@}
71
72 /// @name Setters
73 /// @see @ref set_ops "Setting Ops"
74 ///@{
75 using Setters<Pi>::set;
76 Pi* set(const Def* dom, const Def* codom) { return Def::set({dom, codom})->as<Pi>(); }
77 Pi* set_dom(const Def* dom) { return Def::set(0, dom)->as<Pi>(); }
78 Pi* set_dom(Defs doms);
79 Pi* set_codom(const Def* codom) { return Def::set(1, codom)->as<Pi>(); }
80 Pi* unset() { return Def::unset()->as<Pi>(); }
81 ///@}
82
83 /// @name Type Checking
84 ///@{
85 const Def* check(size_t, const Def*) final;
86 const Def* check() final;
87 static const Def* infer(const Def* dom, const Def* codom);
88 ///@}
89
90 /// @name Rebuild
91 ///@{
92 Pi* stub(const Def* type) { return stub_(world(), type)->set(dbg()); }
93 const Pi* immutabilize() final;
94 const Def* reduce(const Def* arg) const { return Def::reduce(arg).front(); }
95 constexpr size_t reduction_offset() const noexcept final { return 1; }
96 ///@}
97
98 static constexpr auto Node = mim::Node::Pi;
99 static constexpr size_t Num_Ops = 2;
100
101private:
102 const Def* rebuild_(World&, const Def*, Defs) const final;
103 Pi* stub_(World&, const Def*) final;
104
105 friend class World;
106};
107
108/// A function.
109/// @see Pi
110class Lam : public Def, public Setters<Lam> {
111private:
112 Lam(const Pi* pi, const Def* filter, const Def* body)
113 : Def(Node, pi, {filter, body}, 0) {}
114 Lam(const Pi* pi)
115 : Def(Node, pi, 2, 0) {}
116
117public:
118 using Filter = std::variant<bool, const Def*>;
119
120 /// @name ops
121 ///@{
122 const Def* filter() const { return op(0); }
123 const Def* body() const { return op(1); }
124 ///@}
125
126 /// @name type
127 /// @anchor lam_dom
128 /// @see @ref proj
129 ///@{
130 const Pi* type() const { return Def::type()->as<Pi>(); }
131 const Def* dom() const { return type()->dom(); }
132 const Def* codom() const { return type()->codom(); }
133 MIM_PROJ(dom, const)
134 MIM_PROJ(codom, const)
135 ///@}
136
137 /// @name Continuations
138 /// @see @ref continuations "Pi: Continuations"
139 ///@{
140 // clang-format off
141 static const Lam* isa_cn(const Def* d) { return Pi::isa_cn(d->type()) ? d->isa<Lam>() : nullptr; }
142 static const Lam* isa_basicblock(const Def* d) { return Pi::isa_basicblock(d->type()) ? d->isa<Lam>() : nullptr; }
143 static const Lam* isa_returning(const Def* d) { return Pi::isa_returning (d->type()) ? d->isa<Lam>() : nullptr; }
144 static Lam* isa_mut_cn(const Def* d) { return isa_cn(d) ? d->isa_mut<Lam>() : nullptr; } ///< Only for mutables.
145 static Lam* isa_mut_basicblock(const Def* d) { return isa_basicblock(d) ? d->isa_mut<Lam>(): nullptr; } ///< Only for mutables.
146 static Lam* isa_mut_returning(const Def* d) { return isa_returning (d) ? d->isa_mut<Lam>(): nullptr; } ///< Only for mutables.
147 // clang-format on
148 ///@}
149
150 /// @name Return Continuation
151 /// @see @ref return_continuation "Pi: Return Continuation"
152 ///@{
153 const Pi* ret_pi() const { return type()->ret_pi(); }
154 const Def* ret_dom() const { return ret_pi()->dom(); }
155 /// Yields the Lam::var of the Lam::ret_pi.
156 const Def* ret_var() { return type()->ret_pi() ? var(num_vars() - 1) : nullptr; }
157 ///@}
158
159 /// @name Setters
160 /// Lam::Filter is a `std::variant<bool, const Def*>` that lets you set the Lam::filter() like this:
161 /// ```cpp
162 /// lam1->app(true, f, arg);
163 /// lam2->app(my_filter_def, f, arg);
164 /// ```
165 /// @note The filter belongs to the *Lam%bda* and **not** the body.
166 /// @see @ref set_ops "Setting Ops"
167 ///@{
168 using Setters<Lam>::set;
169 Lam* set(Filter filter, const Def* body);
170 Lam* set_filter(Filter); ///< Set filter first.
171 Lam* set_body(const Def* body) { return Def::set(1, body)->as<Lam>(); } ///< Set body second.
172 /// Set body to an App of @p callee and @p arg.
173 Lam* app(Filter filter, const Def* callee, const Def* arg);
174 /// Set body to an App of @p callee and @p args.
175 Lam* app(Filter filter, const Def* callee, Defs args);
176 /// Set body to an App of `(f, t)#cond mem` or `(f, t)#cond ()` if @p mem is `nullptr`.
177 Lam* branch(Filter filter, const Def* cond, const Def* t, const Def* f, const Def* arg = nullptr);
178 Lam* set(Defs ops) { return Def::set(ops)->as<Lam>(); }
179 Lam* unset() { return Def::unset()->as<Lam>(); }
180 ///@}
181
182 /// @name Rebuild
183 ///@{
184 Lam* stub(const Def* type) { return stub_(world(), type)->set(dbg()); }
185 using Def::reduce;
186 Defs reduce(Defs) const;
187 const Def* reduce_body(const Def* arg) const { return reduce(arg).back(); }
188 constexpr size_t reduction_offset() const noexcept final { return 0; }
189 ///@}
190
191 /// @name Eta-Conversion
192 ///@{
193 static Lam* eta_expand(Filter, const Def* f);
194 static Lam* eta_expand(const Def* f) { return eta_expand(true, f); } ///< Use `true` Filter.
195 /// Yields body(), if eta-convertible and `nullptr` otherwise.
196 /// η-convertible means: `lm x = body x` where `x` ∉ `body`.
197 const Def* eta_reduce() const;
198 ///@}
199
200 /// @name Type Checking
201 ///@{
202 const Def* check(size_t, const Def*) final;
203 ///@}
204
205 static constexpr auto Node = mim::Node::Lam;
206 static constexpr size_t Num_Ops = 2;
207
208private:
209 const Def* rebuild_(World&, const Def*, Defs) const final;
210 Lam* stub_(World&, const Def*) final;
211
212 friend class World;
213};
214
215/// @name Lam
216/// GIDSet / GIDMap keyed by Lam::gid of `Lam*`.
217///@{
218template<class To>
222///@}
223
224class App : public Def, public Setters<App> {
225private:
226 App(const Axm* axm, u8 curry, u8 trip, const Def* type, const Def* callee, const Def* arg)
227 : Def(Node, type, {callee, arg}, 0) {
228 axm_ = axm;
229 curry_ = curry;
230 trip_ = trip;
231 }
232
233 template<size_t N, bool Callee, bool Args>
234 static auto uncurry_(const Def* callee) {
235 if constexpr (N == std::dynamic_extent) {
236 auto args = DefVec();
237 while (auto app = callee->isa<App>()) {
238 if constexpr (Args) args.emplace_back(app->arg());
239 callee = app->callee();
240 }
241
242 if constexpr (Callee && Args) {
243 std::ranges::reverse(args);
244 return std::pair{callee, args};
245 } else if constexpr (Args) {
246 std::ranges::reverse(args);
247 return args;
248 } else {
249 return callee;
250 }
251 } else {
252 auto args = std::array<const Def*, N>();
253 for (size_t i = N; i-- != 0;) {
254 if (auto app = callee->isa<App>()) {
255 if constexpr (Args) args[i] = app->arg();
256 callee = app->callee();
257 } else {
258 if constexpr (Args) args[i] = nullptr;
259 }
260 }
261
262 if constexpr (Callee && Args)
263 return std::pair{callee, args};
264 else if constexpr (Args)
265 return args;
266 else
267 return callee;
268 }
269 }
270
271public:
272 using Setters<App>::set;
273
274 /// @name callee
275 ///@{
276 const Def* callee() const { return op(0); }
277 const App* decurry() const { return callee()->as<App>(); } ///< Returns App::callee again as App.
278 const Pi* callee_type() const { return callee()->type()->as<Pi>(); }
279 ///@}
280
281 /// @name arg
282 /// @anchor app_arg
283 /// @see @ref proj
284 ///@{
285 const Def* arg() const { return op(1); }
286 MIM_PROJ(arg, const)
287 ///@}
288
289 /// @name Get axm, current curry counter and trip count
290 ///@{
291 const Axm* axm() const { return axm_; }
292 u8 curry() const { return curry_; }
293 u8 trip() const { return trip_; }
294 ///@}
295
296 /// @name Uncurry
297 /// Retrieve all App::arg%s of a curried App.
298 /// Use like this:
299 /// ```
300 /// // 1. Variant:
301 /// auto [abc, de] = app->uncurry<2>();
302 /// auto [a, b, c] = abc->projs<3>();
303 /// auto [d, e] = de->projs<2>();
304 ///
305 /// // 2. Variant:
306 /// auto [callee , args] = App::uncurry(def);
307 ///
308 /// ```
309 /// @returns
310 /// 1. Variant: <br>
311 /// *only* the arguments in a `std::array<const Def*, N>`.
312 /// You will *not* retrieve the initial callee because if you know the number of curried App%s,
313 /// you probably also know the callee anyway.
314 /// Also, if you "overshoot" the number of curried App%s, the superflous args on the left will be set to
315 /// `nullptr`.
316 /// 2. Variant: <br>
317 /// A pair that contains:
318 /// 1. The initial callee.
319 /// 2. A DefVec of all curried App::arg%s.
320 /// You can enforce variant 1 / variant 2 by with the template argument @p Callee.
321 ///@{
322 // clang-format off
323 template<size_t N = std::dynamic_extent> static auto uncurry(const Def* def) { return uncurry_<N, true, true >(def ); }
324 template<size_t N = std::dynamic_extent> auto uncurry() const { return uncurry_<N, true, true >(this); }
325
326 static const Def* uncurry_callee(const Def* def) { return uncurry_<std::dynamic_extent, true, false>(def ); }
327 const Def* uncurry_callee() const { return uncurry_<std::dynamic_extent, true, false>(this); }
328
329 template<size_t N = std::dynamic_extent> static auto uncurry_args(const Def* def) { return uncurry_<N, false, true>(def ); }
330 template<size_t N = std::dynamic_extent> auto uncurry_args() const { return uncurry_<N, false, true>(this); }
331 // clang-format on
332 ///@}
333
334 static constexpr auto Node = mim::Node::App;
335 static constexpr size_t Num_Ops = 2;
336
337private:
338 const Def* rebuild_(World&, const Def*, Defs) const final;
339
340 friend class World;
341};
342
343/// @name Helpers to work with Functions
344///@{
345inline const App* isa_callee(const Def* def, size_t i) { return i == 0 ? def->isa<App>() : nullptr; }
346
347/// These are Lam%s that are
348/// * neither `nullptr`,
349/// * nor Lam::is_external,
350/// * nor Lam::is_annex,
351/// * nor Lam::is_unset.
352inline Lam* isa_optimizable(Lam* lam) {
353 if (!lam || lam->is_external() || lam->is_annex() || !lam->is_set()) return nullptr;
354 return lam;
355}
356
357inline std::pair<const App*, Lam*> isa_apped_mut_lam(const Def* def) {
358 if (auto app = def->isa<App>()) return {app, app->callee()->isa_mut<Lam>()};
359 return {nullptr, nullptr};
360}
361
362/// The high level view is:
363/// ```
364/// f: B -> C
365/// g: A -> B
366/// f o g := λ x. f(g(x)) : A -> C
367/// ```
368/// In CPS the types look like:
369/// ```
370/// f: Cn[B, Cn C]
371/// g: Cn[A, Cn B]
372/// h = f o g
373/// h: Cn[A, cn C]
374/// h = λ (a ret_h) = g (a, h')
375/// h': Cn B
376/// h'= λ b = f (b, ret_h)
377/// ```
378const Def* compose_cn(const Def* f, const Def* g);
379///@}
380
381} // namespace mim
const Axm * axm() const
Definition lam.h:291
static constexpr size_t Num_Ops
Definition lam.h:335
static const Def * uncurry_callee(const Def *def)
Definition lam.h:326
const Pi * callee_type() const
Definition lam.h:278
u8 curry() const
Definition lam.h:292
const App * decurry() const
Returns App::callee again as App.
Definition lam.h:277
static constexpr auto Node
Definition lam.h:334
friend class World
Definition lam.h:340
auto uncurry() const
Definition lam.h:324
static auto uncurry(const Def *def)
Definition lam.h:323
const Def * callee() const
Definition lam.h:276
static auto uncurry_args(const Def *def)
Definition lam.h:329
const Def * uncurry_callee() const
Definition lam.h:327
auto uncurry_args() const
Definition lam.h:330
const Def * arg() const
Definition lam.h:285
u8 trip() const
Definition lam.h:293
const Def * rebuild_(World &, const Def *, Defs) const final
Definition def.cpp:114
Definition axm.h:9
Base class for all Defs.
Definition def.h:251
bool is_set() const
Yields true if empty or the last op is set.
Definition def.cpp:297
constexpr Node node() const noexcept
Definition def.h:274
Def * set(size_t i, const Def *)
Successively set from left to right.
Definition def.cpp:265
u8 trip_
Definition def.h:670
World & world() const noexcept
Definition def.cpp:438
virtual const Def * check()
After all Def::ops have ben Def::set, this method will be invoked to check the type of this mutable.
Definition def.h:584
constexpr auto ops() const noexcept
Definition def.h:305
constexpr flags_t flags() const noexcept
Definition def.h:269
u8 curry_
Definition def.h:669
const Def * op(size_t i) const noexcept
Definition def.h:308
nat_t num_vars() noexcept
Definition def.h:429
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.cpp:446
bool is_external() const noexcept
Definition def.h:467
const Def * var()
Not necessarily a Var: E.g., if the return type is [], this will yield ().
Definition def.cpp:309
flags_t flags_
Definition def.h:668
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
Definition def.cpp:288
constexpr auto reduce(const Def *arg) const
Definition def.h:564
Dbg dbg() const
Definition def.h:506
bool is_annex() const noexcept
Definition def.h:471
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:206
A function.
Definition lam.h:110
std::variant< bool, const Def * > Filter
Definition lam.h:118
Lam * unset()
Definition lam.h:179
const Def * filter() const
Definition lam.h:122
static const Lam * isa_cn(const Def *d)
Definition lam.h:141
static constexpr size_t Num_Ops
Definition lam.h:206
Lam * set(Filter filter, const Def *body)
Definition lam.cpp:29
static Lam * eta_expand(Filter, const Def *f)
Definition lam.cpp:51
Lam * set_filter(Filter)
Set filter first.
Definition lam.cpp:28
static Lam * isa_mut_basicblock(const Def *d)
Only for mutables.
Definition lam.h:145
Defs reduce(Defs) const
Definition lam.cpp:39
static const Lam * isa_returning(const Def *d)
Definition lam.h:143
constexpr size_t reduction_offset() const noexcept final
First Def::op that needs to be dealt with during reduction; e.g.
Definition lam.h:188
const Pi * ret_pi() const
Definition lam.h:153
const Def * dom() const
Definition lam.h:131
static Lam * isa_mut_returning(const Def *d)
Only for mutables.
Definition lam.h:146
const Pi * type() const
Definition lam.h:130
Lam * set(Defs ops)
Definition lam.h:178
Lam * stub_(World &, const Def *) final
Definition def.cpp:153
static constexpr auto Node
Definition lam.h:205
friend class World
Definition lam.h:212
const Def * eta_reduce() const
Yields body(), if eta-convertible and nullptr otherwise.
Definition lam.cpp:43
static const Lam * isa_basicblock(const Def *d)
Definition lam.h:142
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:35
Lam * app(Filter filter, const Def *callee, const Def *arg)
Set body to an App of callee and arg.
Definition lam.cpp:30
const Def * rebuild_(World &, const Def *, Defs) const final
Definition def.cpp:119
const Def * ret_dom() const
Definition lam.h:154
Lam * set_body(const Def *body)
Set body second.
Definition lam.h:171
Lam * stub(const Def *type)
Definition lam.h:184
const Def * body() const
Definition lam.h:123
static Lam * isa_mut_cn(const Def *d)
Only for mutables.
Definition lam.h:144
static Lam * eta_expand(const Def *f)
Use true Filter.
Definition lam.h:194
const Def * reduce_body(const Def *arg) const
Definition lam.h:187
const Def * ret_var()
Yields the Lam::var of the Lam::ret_pi.
Definition lam.h:156
const Def * codom() const
Definition lam.h:132
A dependent function type.
Definition lam.h:14
constexpr size_t reduction_offset() const noexcept final
First Def::op that needs to be dealt with during reduction; e.g.
Definition lam.h:95
const Def * ret_dom() const
Pi::domain of Pi::ret_pi.
Definition lam.h:69
const Def * check() final
After all Def::ops have ben Def::set, this method will be invoked to check the type of this mutable.
Definition check.cpp:325
Pi(const Def *type, bool implicit)
Constructor for a mutable Pi.
Definition lam.h:20
Pi * unset()
Definition lam.h:80
static const Pi * has_ret_pi(const Def *d)
Yields the Pi::ret_pi() of d, if it is in fact a Pi.
Definition lam.h:59
Pi * set_codom(const Def *codom)
Definition lam.h:79
static constexpr size_t Num_Ops
Definition lam.h:99
Pi * make_implicit()
Definition lam.h:27
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)
Is this a continuation - i.e. is the Pi::codom mim::Bottom?
Definition lam.h:47
static const Pi * isa_basicblock(const Def *d)
Is this a continuation (Pi::isa_cn) that is not Pi::isa_returning?
Definition lam.h:51
static const Def * infer(const Def *dom, const Def *codom)
Definition check.cpp:318
bool is_implicit() const
Definition lam.h:26
Pi * make_explicit()
Definition lam.h:28
const Def * dom() const
Definition lam.h:35
friend class World
Definition lam.h:105
const Def * codom() const
Definition lam.h:36
static Pi * isa_implicit(const Def *d)
Is d an Pi::is_implicit (mutable) Pi?
Definition lam.h:54
Pi * set(const Def *dom, const Def *codom)
Definition lam.h:76
static constexpr auto Node
Definition lam.h:98
const Def * reduce(const Def *arg) const
Definition lam.h:94
const Pi * immutabilize() final
Tries to make an immutable from a mutable.
Definition def.cpp:190
Pi * stub(const Def *type)
Definition lam.h:92
const Def * rebuild_(World &, const Def *, Defs) const final
Definition def.cpp:123
const Pi * ret_pi() const
Yields the last Pi::dom, if Pi::isa_basicblock.
Definition lam.cpp:13
Pi * stub_(World &, const Def *) final
Definition def.cpp:155
Pi * set_dom(const Def *dom)
Definition lam.h:77
static const Pi * isa_returning(const Def *d)
Is this a continuation (Pi::isa_cn) which has a Pi::ret_pi?
Definition lam.h:49
CRTP-based mixin to declare setters for Def::loc & Def::name using a covariant return type.
Definition def.h:195
#define MIM_PROJ(NAME, CONST)
Use as mixin to wrap all kind of Def::proj and Def::projs variants.
Definition def.h:164
Definition ast.h:14
View< const Def * > Defs
Definition def.h:76
Vector< const Def * > DefVec
Definition def.h:77
GIDSet< Lam * > LamSet
Definition lam.h:220
u64 flags_t
Definition types.h:45
std::pair< const App *, Lam * > isa_apped_mut_lam(const Def *def)
Definition lam.h:357
Lam * isa_optimizable(Lam *lam)
These are Lams that are.
Definition lam.h:352
absl::flat_hash_map< K, V, GIDHash< K > > GIDMap
Definition util.h:195
LamMap< Lam * > Lam2Lam
Definition lam.h:221
GIDMap< Lam *, To > LamMap
Definition lam.h:219
const App * isa_callee(const Def *def, size_t i)
Definition lam.h:345
const Def * compose_cn(const Def *f, const Def *g)
The high level view is:
Definition lam.cpp:62
absl::flat_hash_set< K, GIDHash< K > > GIDSet
Definition util.h:196
uint8_t u8
Definition types.h:34
@ Pi
Definition def.h:114
@ Bot
Definition def.h:114
@ Lam
Definition def.h:114
@ App
Definition def.h:114