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