MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
world.cpp
Go to the documentation of this file.
1#include "mim/world.h"
2
3#include <cctype>
4
5#include <algorithm>
6
7#include <fe/container.h>
8#include <fe/restore.h>
9#include <fe/worklist.h>
10
11#include "mim/check.h"
12#include "mim/def.h"
13#include "mim/driver.h"
14#include "mim/rewrite.h"
15#include "mim/tuple.h"
16
17#include "mim/util/gid.h"
18
19namespace mim {
20
21namespace {
22
23/// Arity `d#index ← val` unrolls a Pack @p d into; `std::nullopt` if the Pack rule in World::insert doesn't fire.
24/// A fused Pack is off limits: its Def::arity is only the front axis, while Pack::body is still the scalar.
25std::optional<nat_t> pack_unrolls(const Def* d, const Def* index, u64 threshold) {
26 if (!Lit::isa(index)) return {};
27 if (auto pack = d->isa<Pack>(); pack && !pack->shape().is_fused())
28 if (auto a = Lit::isa(pack->arity()); a && *a < threshold) return a;
29 return {};
30}
31
32// TODO I'm not too happy with this solutin.
33// Once we switch to a mostly immutable IR, we can hopefully detect this better
34/// A curried Lam whose innermost body is still being emitted; reducing it would copy that empty Lam for good.
35bool has_unset_curry(const Lam* lam) {
36 LamSet seen; // a Pi may be its own codomain, so the chain may cycle
37 for (auto body = lam->body(); auto inner = body->isa_mut<Lam>(); body = inner->body()) {
38 if (!inner->is_set()) return true;
39 if (!seen.emplace(inner).second) break;
40 }
41 return false;
42}
43
44/// Does `d#index ← val` rebuild an aggregate right away? Mirrors the two folding rules in World::insert.
45bool insert_rebuilds(const Def* d, const Def* index, u64 threshold) {
46 if (d->isa<Tuple>()) return bool(Lit::isa(index));
47 return bool(pack_unrolls(d, index, threshold));
48}
49
50/// How many axes @p type indexes in one go; `1` for anything that isn't an Arr, `std::nullopt` for a dynamic rank.
51std::optional<nat_t> own_rank(const Def* type) {
52 if (auto arr = type->isa<Arr>()) return arr->shape().rank();
53 return 1;
54}
55
56/// `(t#is)#js` as *one* index into @p t; `nullptr` unless @p t's own shape has room for both ranks.
57/// The Extract and the Insert rule are duals and share this check.
58Shape fused_index(const Def* t, Shape is, Shape js) {
59 auto arr = t->unfold_type()->isa<Arr>();
60 if (!arr) return {};
61 auto ri = is.rank();
62 auto rj = js.rank();
63 auto rt = arr->shape().rank();
64 if (!ri || !rj || !rt || *ri + *rj > *rt) return {};
65 return is + js;
66}
67
68/// Coerces @p val to @p elem_type or bails out with the "not assignable" diagnostic.
69const Def* assign_or_bail(const Def* elem_type, const Def* val) {
70 auto res = Check::coerce(elem_type, val);
71 if (!res)
72 val->blame("value is not assignable to element type")
73 .n("expected `{}`, got `{}`", elem_type, type_of(val))
74 .bail();
75 return res;
76}
77
78/// Checks the multi-dimensional @p index against @p d and yields the type it peels to.
79const Def* nary_elem_type(const Def* d, Shape index) {
80 auto type = d->unfold_type();
81 auto arr = type->isa<Arr>();
82 if (!arr) index->blame("multi-dimensional index expects an array but got `{}`", type).bail();
83
84 auto shape = arr->shape();
85 auto ri = index.rank();
86 auto rt = shape.rank();
87 // A *partial* index peels only its own axes and yields a sub-array.
88 auto fit = ri && rt ? *ri <= *rt : Check::alpha(index->arity(), shape->arity());
89 if (!fit)
90 index->blame("index of rank `{}` does not fit shape `{}` of rank `{}`", index->arity(), *shape, shape->arity())
91 .bail();
92
93 // Only the axes World::extract folds away reach World::extract1's check, so the ones staying fused
94 // need it here. A fused shape never binds the Arr's own var, so its extents compare as they stand.
95 if (ri && rt)
96 for (nat_t i = 0; i != *ri; ++i)
97 if (auto size = Idx::isa(index[i]->unfold_type()); size && !Check::alpha(shape[i], size))
98 index[i]->blame("index does not fit within arity `{}`", shape[i]).bail();
99
100 return d->world().peel(arr, *index);
101}
102
103/// A fresh mutable Seq of @p seq's kind holding @p elem at @p shape, whose body @p rest computes from its own var.
104const Def* mut_shaped(const Seq* seq, Shape shape, const Def* elem, auto rest) {
105 auto& w = seq->world();
106 auto is_pack = seq->is_intro();
107 auto res = w.mut_seq(is_pack, is_pack ? w.arr(shape, elem) : seq->type());
108 res->Def::set(0, *shape);
109 return res->Def::set(1, rest(res))->zonk_mut();
110}
111
112/// Marks a World::Reduct slot while it is being computed: seeing it again means the op requires its own reduction.
113const Def* const Filling = (const Def*)1;
114
115/// Sorts by gid and drops duplicates; Def%s are hash-consed, so pointer identity *is* structural identity.
116void sort_unique(DefVec& defs) {
117 std::ranges::sort(defs, GIDLt<const Def*>());
118 defs.erase(std::unique(defs.begin(), defs.end()), defs.end());
119}
120
121} // namespace
122
124 assert(!def->is_external());
125 assert(def->is_closed());
126 def->external_ = true;
127 fe::assert_emplace(sym2mut_, def->sym(), def);
128}
129
131 assert(def->is_external());
132 def->external_ = false;
133 auto num = sym2mut_.erase(def->sym());
134 assert_unused(num == 1);
135}
136
137const Def* World::Annexes::attach(flags_t flags, Sym sym, const Def* def) {
138 driver().log().t("register annex `{}` 0x{:x} → {}", sym, flags, def);
139 if (driver().is_loaded(Annex::demangle(flags))) {
140 fe::assert_emplace(flags2entry_, flags, Annexes::Entry{sym, def});
141 fe::assert_emplace(sym2flags_, sym, flags);
142 def2flags_.emplace(def, flags);
143 def->annex_ = true;
144 return def;
145 }
146 return nullptr;
147}
148
150 if (!driver().is_loaded(Annex::demangle(flags))) return;
151 // An alias spelled the same as its target's own (unqualified) name registers the identical
152 // qualified string as the target - a benign no-op, not a conflict.
153 if (auto [i, ins] = sym2flags_.try_emplace(sym, flags); !ins) assert(i->second == flags);
154}
155
156/*
157 * Library
158 */
159
160const Def* World::annex(Sym sym) {
161 if (auto flags = fe::lookup(annexes().sym2flags(), sym)) return annex(*flags);
162 if (auto lib = driver().library())
163 if (auto flags = fe::lookup(lib->annexes().sym2flags(), sym)) return annex(*flags);
164 return nullptr;
165}
166
167const Def* World::annex_miss(flags_t flags) {
168 if (auto lib = driver().library(); lib && lib != this)
169 if (auto e = fe::lookup(lib->annexes().flags2entry(), flags)) {
170 log().d("import annex `{}`", e->sym);
171 return annexes().attach(flags, e->sym, import(e->def));
172 }
173
174 fe::throwf("no Axm with ID {}; is plugin `{}` loaded?", flags, Annex::demangle(flags));
175}
176
177/*
178 * constructor & destructor
179 */
180
182 : driver_(driver)
183 , zonker_(*this)
184 , state_(state)
185 , importer_(*this)
186 , move_(driver) {
187 data_.type_level = insert<Level>(*this);
188 data_.lit_level_0 = lit(type_level(), 0);
189 data_.lit_level_1 = lit(type_level(), 1);
190 data_.type_0 = unify<Type>(lit_level_0());
191 data_.type_1 = unify<Type>(lit_level_1());
192 data_.type_bot = insert<Bot>(type());
193 data_.type_top = insert<Top>(type());
194 data_.sigma = unify<Sigma>(type(), Defs{});
195 data_.tuple = unify<Tuple>(sigma(), Defs{});
196 data_.type_nat = insert<mim::Nat>(*this);
197 data_.type_idx = insert<mim::Idx>(pi(type_nat(), type()));
198 // A frozen World cannot create them on demand, but e.g. Def::num_tprojs needs them to compute an arity.
199 for (nat_t i = 0; i != Num_Lit_Nats; ++i)
200 data_.lit_nats[i] = lit(type_nat(), i);
201 data_.lit_idx_1_0 = lit_idx(1, 0);
202 data_.type_bool = type_idx(2);
203 data_.lit_bool[0] = lit_idx(2, 0_u64);
204 data_.lit_bool[1] = lit_idx(2, 1_u64);
205 data_.lit_nat_max = lit_nat(nat_t(-1));
206}
207
210
211// ~Def() has nothing to do, so World does not run it.
212World::~World() = default;
213
214static_assert(std::is_trivially_destructible_v<Dbg> && std::is_trivially_destructible_v<Vars>
215 && std::is_trivially_destructible_v<Muts> && std::is_trivially_destructible_v<NormalizeFn>,
216 "a Def member gained a non-trivial destructor: World::~World must destroy Defs again");
217
218/*
219 * Driver
220 */
221
222fe::Error& World::error() { return driver().error(); }
223const fe::Error& World::error() const { return driver().error(); }
224const fe::Log& World::log() const { return driver().log(); }
225Flags& World::flags() { return driver().flags(); }
226
227Sym World::sym(const char* s) { return driver().sym(s); }
228Sym World::sym(std::string_view s) { return driver().sym(s); }
229Sym World::sym(const std::string& s) { return driver().sym(s); }
230
231/*
232 * factory methods
233 */
234
235const Type* World::type(const Def* level) {
236 if (!level) return nullptr;
237 level = level->zonk();
238
239 if (!level->isa_type<Level>())
240 level->blame("argument to `Type` must be of type `Level` but is of type `{}`", type_of(level)).bail();
241
242 if (level == lit_level_0()) return data_.type_0;
243 if (level == lit_level_1()) return data_.type_1;
244 return unify<Type>(level);
245}
246
247const Def* World::lsucc(const Def* op, level_t offset) {
248 op = op->zonk();
249
250 if (!op->isa_type<Level>())
251 op->blame("operand of a level successor must be of type `Level` but is of type `{}`", type_of(op)).bail();
252
253 if (offset == 0) return op;
254 if (auto l = Lit::isa(op)) return lit_level(*l + offset);
255 if (auto inc = op->isa<LSucc>()) return lsucc(inc->op(), inc->offset() + offset);
256 if (auto max = op->isa<LMax>()) return lmax(DefVec(max->ops(), [&](const Def* op) { return lsucc(op, offset); }));
257 return unify<LSucc>(op, offset);
258}
259
260const Def* World::lmax(Defs levels) {
261 level_t lvl = 0;
262 auto ops = DefVec();
263 auto push = [&](const Def* op) {
264 if (auto l = Lit::isa(op))
265 lvl = std::max(lvl, *l);
266 else
267 ops.emplace_back(op);
268 };
269
270 for (auto level : levels) {
271 level = level->zonk();
272 if (!level->isa_type<Level>())
273 level->blame("operand of a level max must be of type `Level` but is of type `{}`", type_of(level)).bail();
274
275 // A zonked LMax is already flat, so one level suffices.
276 if (auto max = level->isa<LMax>())
277 for (auto op : max->ops())
278 push(op);
279 else
280 push(level);
281 }
282
283 if (lvl > 0 || ops.empty()) ops.emplace_back(lit_level(lvl));
284 sort_unique(ops);
285 return ops.size() == 1 ? ops.front() : unify<LMax>(*this, ops);
286}
287
288static void default_level(const Def* level) {
289 if (auto hole = Hole::isa_unset(level))
290 hole->set(hole->world().lit_level(0));
291 else if (auto inc = level->isa<LSucc>())
292 default_level(inc->op()->zonk());
293 else if (auto max = level->isa<LMax>())
294 for (auto op : max->ops())
295 default_level(op->zonk());
296}
297
298// The value of @p level with all its unset Hole%s read as `0`.
299static level_t eval0(const Def* level) {
300 level = level->zonk();
301 if (auto l = Lit::isa(level)) return *l;
302 if (auto succ = level->isa<LSucc>()) return eval0(succ->op()) + succ->offset();
303 if (auto max = level->isa<LMax>()) {
304 auto res = level_t(0);
305 for (auto op : max->ops())
306 res = std::max(res, eval0(op));
307 return res;
308 }
309 assert(Hole::isa_unset(level));
310 return 0;
311}
312
314 // Retry first: Hole%s solved in the meantime may have turned a constraint into one that determines the rest.
315 for (auto eq : std::exchange(move_.levels, {})) {
316 auto _ = push(eq.loc);
317 if (!Check::alpha(eq.lmax, eq.level))
318 error().e(eq.loc, "universe level `{}` does not match `{}`", eq.lmax->zonk(), eq.level->zonk()).bail();
319 }
320
321 auto eqs = std::exchange(move_.levels, {});
322 for (auto eq : eqs)
323 if (eval0(eq.lmax) != eval0(eq.level))
324 error()
325 .e(eq.loc, "ambiguous universe levels: `{}` = `{}` has no unique solution", eq.lmax->zonk(),
326 eq.level->zonk())
327 .bail();
328
329 for (auto eq : eqs) {
330 default_level(eq.lmax->zonk());
331 default_level(eq.level->zonk());
332 }
333}
334
335// An untyped binder used as a type has a Hole as sort, which must be a Type of some yet unknown level.
336static const Def* level_of(const Def* type) {
337 if (auto kind = type->zonk()->unfold_type()) {
338 kind = kind->zonk();
339 if (auto t = kind->isa<Type>()) return t->level();
340 if (auto hole = Hole::isa_unset(kind)) {
341 auto& w = type->world();
342 auto level = w.mut_hole_level();
343 if (Check::alpha(hole, w.type(level))) return level;
344 }
345 }
346 type->blame("`{}` is not a type", type).bail();
347}
348
349const Def* World::level(Defs types) {
350 auto lvl = level_t(0);
351 auto levels = DefVec();
352 for (auto type : types) {
353 auto level = level_of(type);
354 if (auto l = Lit::isa(level))
355 lvl = std::max(lvl, *l);
356 else
357 levels.emplace_back(level);
358 }
359
360 if (levels.empty()) return lit_level(lvl);
361 levels.emplace_back(lit_level(lvl));
362 return lmax(levels);
363}
364
365const Def* World::inhabitant_(const Def* type) {
366 switch (type->node()) {
367 case Node::Single: return wrap(type->as<Single>()->op());
368 case Node::Sigma: {
369 auto sigma = type->as<Sigma>();
370 if (sigma->num_ops() == 0) return tuple(sigma, {});
371 if (sigma->isa_mut()) return nullptr;
372 auto ops = DefVec();
373 for (auto op : sigma->ops())
374 if (auto i = inhabitant(op))
375 ops.emplace_back(i);
376 else
377 return nullptr;
378 return tuple(sigma, ops);
379 }
380 case Node::Arr:
381 if (auto arr = type->isa_imm<Arr>())
382 if (auto i = inhabitant(arr->body())) return pack(arr->shape(), i);
383 return nullptr;
384 case Node::Variant:
385 if (auto lone = Variant::isa_lone(type))
386 if (auto i = inhabitant(lone)) return inj(type, 0, i);
387 return nullptr;
388 default: return nullptr;
389 }
390}
391
392const Def* World::var(Def* mut) {
393 if (auto var = mut->var_) return var;
394
395 if (auto var_type = mut->var_type()) // could be nullptr, if frozen
396 if (auto inhabitant = this->inhabitant(var_type)) return inhabitant;
397
398 return mut->var_ = unify<Var>(mut);
399}
400
401const Def* World::implicit_holes(const Def* callee) {
402 while (auto type = callee->unfold_type()) // Level has no type at all and takes no implicit arguments
403 if (auto pi = Pi::isa_implicit(type->zonk()))
404 callee = app(callee, mut_hole(pi->dom()));
405 else
406 break;
407 return callee;
408}
409
410template<bool Normalize>
411const Def* World::implicit_app(const Def* callee, const Def* arg) {
412 return app<Normalize>(implicit_holes(callee), arg);
413}
414
415template<bool Normalize>
416const Def* World::app(const Def* callee, const Def* arg) {
417 callee = callee->zonk();
418 arg = arg->zonk();
419
420 // Any other hash-consed callee keeps the Loc of wherever it was first built, if any.
421 auto declared_here = [callee](fe::Error& err) -> fe::Error& {
422 if (callee->isa_mut() || callee->isa<Var>() || callee->isa<Lam>() || callee->isa<Axm>())
423 err.n(callee->loc(), "callee `{}` declared here", callee);
424 return err;
425 };
426
427 auto pi = callee->isa_type<Pi>();
428 if (!pi)
429 declared_here(
430 callee->blame("callee is not of function type").n("callee `{}` has type `{}`", callee, type_of(callee)))
431 .bail();
432
433 auto new_arg = Check::coerce(pi->dom(), arg);
434 if (!new_arg && is_lenient()) new_arg = arg;
435 if (!new_arg)
436 declared_here(arg->blame("argument is not assignable to callee's domain")
437 .n("expected `{}`, got `{}`", pi->dom(), type_of(arg)))
438 .bail();
439
440 // re-zonk after assignable check above - we might have inferred new stuff
441 arg = new_arg->zonk();
442 callee = callee->zonk();
443 pi = callee->isa_type<Pi>();
444
445 // always β-reduce non-recursive, non-parametric lambdas
446 if (auto imm = callee->isa_imm<Lam>()) return imm->body();
447
448 if (auto lam = callee->isa_mut<Lam>(); lam && lam->is_set()) {
449 auto var = lam->has_var();
450
451 // Applying a Lam to its own Var is the identity substitution, so it resolves to the body.
452 // This unfolds a self-application / fixed-point reference.
453 if (var && arg == var) return lam->body();
454
455 // β-reduce or partially evaluate a set, mutable Lam.
456 if (lam->filter() != lit_ff() && !has_unset_curry(lam)) {
457 if (!var) {
458 if (lam->filter() == lit_tt()) return lam->body();
459 } else if (is_frozen()) {
460 if (cached_reduct(var, arg, 0) == lit_tt())
461 if (auto body = cached_reduct(var, arg, 1)) return body;
462 } else if (reduce(var, arg, 0) == lit_tt()) {
463 // Re-entering a body that is still being reduced for this very arg would not terminate.
464 auto i = move_.substs.find({var, arg});
465 auto body = i != move_.substs.end() ? i->second->ops()[1] : nullptr;
466 if (body != Filling) {
467 if (body) return reduce(var, arg, 1);
468 if (data_.pe_depth == flags().pe_fuel)
469 callee->blame("partial evaluation of `{}` ran out of fuel", lam)
470 .n("nested more than {} partial evaluations; raise the limit with `--pe-fuel`",
471 flags().pe_fuel)
472 .bail();
473 log().d("partially evaluate `{} ({})`", lam, arg);
474 auto _ = fe::Restore(data_.pe_depth, data_.pe_depth + 1);
475 return reduce(var, arg, 1);
476 }
477 }
478 }
479 }
480
481 auto type = pi->reduce(arg)->zonk();
482 callee = callee->zonk();
483 auto [axm, curry, trip] = Axm::next(callee);
484
485 if (axm)
486 if (auto normalizer = axm->normalizer(); Normalize && normalizer && curry == 0)
487 if (auto norm = normalizer(type, callee, arg)) return norm;
488
489 return raw_app(axm, curry, trip, type, callee, arg);
490}
491
492const Def* World::raw_app(const Def* type, const Def* callee, const Def* arg) {
493 type = type->zonk();
494 callee = callee->zonk();
495 arg = arg->zonk();
496
497 auto [axm, curry, trip] = Axm::next(callee);
498 return raw_app(axm, curry, trip, type, callee, arg);
499}
500
501const Def* World::raw_app(const Axm* axm, u8 curry, u8 trip, const Def* type, const Def* callee, const Def* arg) {
502 if (auto inhabitant = this->inhabitant(type)) return inhabitant;
503 return unify<App>(axm, curry, trip, type, callee, arg);
504}
505
506const Def* World::sigma(Defs ops) {
507 auto n = ops.size();
508 if (n == 0) return sigma();
509 if (n == 1) return ops[0]->zonk();
510
511 auto zops = Def::zonk(ops);
512 if (auto uni = Check::is_uniform(zops)) return arr(n, uni);
513 return unify<Sigma>(Sigma::infer(*this, zops), zops);
514}
515
516const Def* World::tuple(Defs ops) {
517 auto n = ops.size();
518 if (n == 0) return tuple();
519 if (n == 1) return ops[0]->zonk();
520
521 auto zops = Def::zonk(ops);
522 for (auto op : zops)
523 if (!op->type()) op->blame("`Level` has no type and cannot be a tuple element").bail();
524 auto sigma = Tuple::infer(*this, zops);
525 // An immutable Sigma means the ops' types differ, so the ops can't be uniform either.
526 auto t = tuple(sigma, zops, !sigma->isa_imm<Sigma>());
527 auto new_t = Check::coerce(sigma, t);
528 if (!new_t) t->blame("tuple of type `{}` is not assignable to inferred type `{}`", type_of(t), sigma).bail();
529
530 return new_t;
531}
532
533const Def* World::tuple(const Def* type, Defs ops) {
534 auto t = tuple(type, ops, true);
535 if (auto tup = t->isa<Tuple>()) check_tuple(tup);
536 return t;
537}
538
539const Def* World::tuple(const Def* type, Defs ops_, bool may_be_uniform) {
540 // TODO type-check type vs inferred type
541 type = type->zonk();
542 auto ops = Def::zonk(ops_);
543
544 auto n = ops.size();
545 if (!type->isa_mut<Sigma>()) {
546 if (n == 0) return tuple();
547 if (n == 1) return ops[0];
548 if (may_be_uniform)
549 if (auto uni = Check::is_uniform(ops)) return pack(n, uni);
550 }
551
552 // eta rule for tuples: (extract(tup, 0), extract(tup, 1), extract(tup, 2)) -> tup
553 if (auto ex0 = n != 0 ? ops[0]->isa<Extract>() : nullptr) {
554 auto tup = ex0->tuple();
555 bool eta = tup->type() == type;
556 for (size_t i = 0; i != n && eta; ++i) {
557 auto ex = ops[i]->isa<Extract>();
558 auto id = ex ? Lit::isa(ex->index()) : std::nullopt;
559 eta = ex && id && *id == u64(i) && ex->tuple() == tup;
560 }
561
562 if (eta) return tup;
563 }
564
565 return unify<Tuple>(type, ops);
566}
567
568void World::check_tuple(const Tuple* t) {
569 auto type = t->type();
570 auto n = t->num_ops();
571 if (!type->isa<Sigma>() && !type->isa<Arr>()) return;
572 if (auto a = Lit::isa(type->arity()); a && *a != n)
573 t->blame("tuple has {} elements but its type `{}` has arity `{}`", n, type, type->arity()).bail();
574
575 for (size_t i = 0; i != n; ++i) {
576 auto expected = component(type, t->ops(), n, i);
577 auto op = t->op(i);
578 if (!Check::coerce(expected, op) && !is_lenient())
579 op->blame("tuple element is not assignable to its type")
580 .n("expected `{}`, got `{}` in `{}`", expected, type_of(op), type)
581 .bail();
582 }
583}
584
585const Def* World::component(const Def* type, Defs parts, size_t n, size_t i) {
586 if (auto var = type->isa_binder<Sigma>().second) {
587 auto args = DefVec(n, [&](size_t k) { return k < parts.size() ? parts[k] : var->proj(n, k); });
588 return reduce(var, tuple(args), i);
589 }
590 if (auto arr = type->isa_imm<Arr>()) return arr->elem();
591 return type->proj(n, i);
592}
593
594const Def* World::tuple(Sym sym) {
595 return tuple(DefVec(sym, [this](char c) { return lit_i8(c); }));
596}
597
598const Def* World::extract(const Def* d, const Def* index_) {
599 if (!d || !index_) return nullptr; // can happen if frozen
600 d = d->zonk();
601
602 auto index = check_index(index_->zonk());
603 if (index.is_dim()) return extract1(d, *index);
604
605 auto r = index.rank();
606 if (!r) return extract_fused(d, index); // a dynamic rank has no axes to fold one by one
607 if (*r == 0) return d; // the index folded away entirely, i.e. `d` is its own only element
608
609 // Fold from the left as far as `d` allows; whatever is left stays *fused*, mirroring `«a, b; T»`.
610 for (nat_t i = 0; i != *r; ++i) {
611 auto c = index[i];
612 auto next = extract1(d, c);
613 if (auto ex = next->isa<Extract>(); ex && ex->tuple() == d && ex->index() == c)
614 return extract_fused(d, index.drop(i));
615 d = next;
616 }
617
618 return d;
619}
620
621/// Validates @p index and folds its literal size-1 axes away, mirroring the ones World::seq folds out of a shape:
622/// `d#(i, 0₁, k)` ≡ `d#(i, k)`. A *scalar* index is left alone - its `Idx 1` carve-out for mutable 1-tuples still
623/// applies - and so is a dynamic rank, which has no axes to inspect.
624Shape World::check_index(const Def* index) {
625 auto type = index->unfold_type(); // nullptr for Level, which the diagnostic below reports
626 if (type && Idx::isa(type)) return index;
627 if (!type || !Shape::isa_indices(type)) {
628 auto& err = index->blame("index must be of `Idx` type but is of type `{}`", type_of(index));
629 if (auto l = Lit::isa(index); l && type == type_nat())
630 err.n("a bare literal is a `Nat`; an index literal carries its arity, e.g. `{}_{}` for an `Idx {}`", *l,
631 *l + 1, *l + 1);
632 err.bail();
633 }
634 return Shape(index).fold();
635}
636
637const Def* World::drop(const Seq* s, nat_t k) {
638 auto shape = s->shape();
639 auto r = shape.rank();
640 if (!r || k > *r || s->has_var()) return nullptr; // a dependent shape needs an index; cf. World::peel
641 if (k == *r) return s->body();
642 if (is_frozen()) return nullptr; // splitting a fused shape builds the sub-Seq
643 return seq(s->is_intro(), shape.drop(k), s->body());
644}
645
646const Def* World::peel(const Seq* seq, const Def* index) {
647 if (!index) return nullptr; // can happen if frozen
648 auto shape = seq->shape();
649 auto rank = shape.rank();
650 auto k = Shape(index).rank();
651 if (!rank || !k || *k >= *rank) return seq->reduce(index); // the index covers every axis
652 if (!seq->has_var()) return drop(seq, *k);
653 if (is_frozen()) return nullptr; // splitting a fused shape builds the sub-Seq
654
655 // Dependent: the leading axes come from @p index, the trailing ones from the result's own index.
656 return mut_shaped(seq, shape.drop(*k), seq->body()->unfold_type(),
657 [&](Seq* res) { return seq->reduce(*(Shape(index) + res->var())); });
658}
659
661 auto outer = seq->shape();
662 auto inner = seq->body() ? seq->body()->zonk()->isa<Seq>() : nullptr;
663 auto var = seq->has_var();
664 // A ragged nest - one whose inner extents depend on the outer index - has no fused shape to spell them in.
665 if (!inner || inner->node() != seq->node() || (var && inner->shape()->has_free_var(var))) return seq->zonk_mut();
666
667 auto ro = outer.rank();
668 auto shape = outer + inner->shape();
669 if (!ro || !shape) return seq->zonk_mut();
670
671 // The fused index supplies both levels: its leading `ro` axes replace the outer one, the rest the inner.
672 return mut_shaped(seq, shape, inner->body()->unfold_type(), [&](Seq* res) {
673 auto v = Shape(res->var());
674 return seq->reduce(*v.take(*ro))->as<Seq>()->reduce(*v.drop(*ro));
675 });
676}
677
679 if (shape.is_dim()) return type_idx(*shape);
680 if (auto r = shape.rank()) return sigma(shape->projs(*r, [this](const Def* e) { return type_idx(e); }));
681
682 // `«i: r; Idx (s#i)»` - a dynamic rank cannot spell out its axes.
683 auto res = mut_arr(type())->set_shape(shape->arity());
684 return res->set_body(type_idx(extract(*shape, res->var())))->zonk_mut();
685}
686
687/// Builds `d#index` for a multi-dimensional @p index that does not fold any further.
688const Def* World::extract_fused(const Def* d, Shape index) {
689 if (index.is_dim()) return extract1(d, *index);
690
691 // An index reaching past `d`'s own shape continues through the element type - `«2; [Nat, Bool]»` is rank 1,
692 // yet `t#(i, j)` is still `t#i#j`. Split it at the boundary, so each Extract matches the shape it indexes.
693 auto ri = index.rank();
694 auto rt = own_rank(d->unfold_type());
695 if (ri && rt && *ri > *rt && !is_frozen()) return extract(extract_fused(d, index.take(*rt)), *index.drop(*rt));
696
697 // `d#is ← val` then `#is` -> `val`; cf. the same rule in World::extract1
698 if (auto insert = d->isa<Insert>(); insert && insert->index() == *index) return insert->value();
699 return unify_elim<Extract>(nary_elem_type(d, index), d, *index);
700}
701
702/// Type-checks a *scalar* @p index of `Idx size` against @p type.
703/// @returns `true` if the axis has folded out of @p type - `«1; T»` ≡ `T` - so that `d#index` *is* `d` and
704/// `d#index ← val` replaces all of it.
705bool World::is_folded_axis(const Def* type, const Def* index, const Def* size) {
706 if (Lit::isa(size) == 1) {
707 // A *mutable* Sigma may be a genuine 1-tuple and still needs a real Extract/Insert; TODO mutable Arr?
708 auto sigma = type->isa_mut<Sigma>();
709 if (!sigma || sigma->num_ops() != 1) return true;
710 }
711
712 if (!Check::alpha(type->arity(), size)) index->blame("index does not fit within arity `{}`", type->arity()).bail();
713 return false;
714}
715
716const Def* World::extract1(const Def* d, const Def* index) {
717 auto index_ty = index->unfold_type();
718 auto size = Idx::isa(index_ty);
719 auto lidx = Lit::isa(index);
720 auto type = d->unfold_type();
721 if (!type) d->blame("`Level` has no type and hence no elements to extract").bail();
722 type = type->zonk(); // a Var's type may still be a since-resolved Hole of its binder
723
724 if (is_folded_axis(type, index, size)) return d;
725
726 if (auto pack = d->isa<Pack>()) {
727 if (pack->shape().is_fused()) return peel(pack, index); // one axis off a fused Pack yields a sub-Pack
728 return pack->reduce(index);
729 }
730
731 // extract(insert(x, index, val), index) -> val
732 if (auto insert = d->isa<Insert>()) {
733 if (index == insert->index()) return insert->value();
734 }
735
736 // `(t#is)#j` -> `t#(is, j)`: the maximal-rank Extract into one fused Arr is the normal form.
737 if (auto ex = is_frozen() ? nullptr : d->isa<Extract>())
738 if (auto is = fused_index(ex->tuple(), ex->index(), index)) return extract_fused(ex->tuple(), is);
739
740 if (lidx) {
741 // A Hole only tuplefies into a known number of components; a dynamic rank leaves a symbolic Extract.
742 if (auto hole = d->isa_mut<Hole>())
743 if (auto n = Idx::isa_lit(index_ty)) d = hole->tuplefy(*n);
744 if (auto tuple = d->isa<Tuple>()) return tuple->op(*lidx);
745
746 // extract(insert(x, j, val), i) -> extract(x, i) where i != j (guaranteed by rule above)
747 if (auto insert = d->isa<Insert>()) {
748 if (insert->index()->isa<Lit>()) return extract(insert->tuple(), index);
749 }
750
751 if (auto sigma = type->isa<Sigma>()) {
752 if (auto var = sigma->has_var()) {
753 if (d == var) return unify_elim<Extract>(sigma->op(*lidx), d, index); // `var -> var` is the identity
754 // Frozen, only an already cached reduct can be replayed - rewriting would create nodes.
755 if (is_frozen()) {
756 auto t = cached_reduct(var, d, *lidx);
757 return t ? unify_elim<Extract>(t, d, index) : nullptr;
758 }
759 return unify_elim<Extract>(reduce(var, d, *lidx), d, index);
760 }
761
762 return unify_elim<Extract>(sigma->op(*lidx), d, index);
763 }
764 }
765
766 const Def* elem_t;
767 if (auto arr = type->isa<Arr>()) {
768 elem_t = peel(arr, index);
769 } else {
770 auto sigma = type->as<Sigma>();
771 elem_t = nullptr;
772 // «(a_0, ..., a_{n-1})#index; body» is more precise than the join if all ops are Arrs of the same body.
773 if (sigma->isa_imm()) {
774 const Def* body = nullptr;
775 auto extents = DefVec();
776 for (auto op : sigma->ops()) {
777 auto op_arr = op->zonk()->isa<Arr>();
778 if (!op_arr || (body && op_arr->body()->zonk() != body)) {
779 extents.clear();
780 break;
781 }
782 body = op_arr->body()->zonk();
783 extents.emplace_back(*op_arr->shape());
784 }
785 if (!extents.empty()) elem_t = this->arr(extract(tuple(extents), index), body);
786 }
787 if (!elem_t) elem_t = join(sigma->ops());
788 }
789
790 if (index->isa<Top>()) {
791 if (auto hole = Hole::isa_unset(d)) {
792 auto elem_hole = mut_hole(elem_t);
793 hole->set(pack(size, elem_hole));
794 return elem_hole;
795 }
796 }
797
798 assert(d);
799 return unify_elim<Extract>(elem_t, d, index);
800}
801
802const Def* World::insert(const Def* d, const Def* index_, const Def* val) {
803 d = d->zonk();
804 val = val->zonk();
805
806 auto type = d->unfold_type();
807 auto index = check_index(index_->zonk());
808
809 // `d#(i, j, k) ← val` stays *fused* unless the outermost write rebuilds an aggregate on the spot.
810 if (!index.is_dim()) {
811 auto r = index.rank();
812 // The index folded away entirely, so the write replaces all of `d`.
813 if (r && *r == 0) return assign_or_bail(type, val);
814
815 // A write deeper than `d`'s own shape reads down to the boundary and writes the levels back out;
816 // cf. World::extract_fused. So does one whose outermost write rebuilds an aggregate.
817 auto rt = own_rank(type);
818 auto cut = r && rt && *r > *rt ? rt
819 : r && insert_rebuilds(d, index[0], flags().scalarize_threshold) ? std::optional<nat_t>(1)
820 : std::nullopt;
821 if (cut) {
822 auto head = *index.take(*cut);
823 return insert(d, head, insert(extract(d, head), *index.drop(*cut), val));
824 }
825
826 auto new_val = assign_or_bail(nary_elem_type(d, index), val);
827
828 // `d#is ← (d#is)#js ← val` -> `d#(is, js) ← val`; cf. the scalar path below, which reaches this
829 // through World::extract's per-axis loop - the fused path has none, so it needs the rule itself.
830 if (auto inner = is_frozen() ? nullptr : new_val->isa<Insert>())
831 if (auto ex = inner->tuple()->isa<Extract>(); ex && ex->tuple() == d && ex->index() == *index)
832 if (auto is = fused_index(d, *index, inner->index())) return insert(d, *is, inner->value());
833
834 // `d#is ← d#is` -> `d` and `(d#is ← y)#is ← val` -> `d#is ← val`; cf. the scalar path below
835 if (auto ex = new_val->isa<Extract>(); ex && ex->tuple() == d && ex->index() == *index) return d;
836 if (auto insert = d->isa<Insert>(); insert && insert->index() == *index) d = insert->tuple();
837 if (auto inhabitant = this->inhabitant(d->type())) return inhabitant;
838 return unify<Insert>(d, *index, new_val);
839 }
840
841 auto size = Idx::isa(index->unfold_type());
842 auto lidx = Lit::isa(*index);
843
844 if (is_folded_axis(type, *index, size)) return assign_or_bail(type, val); // the write replaces all of `d`
845 // A Sigma needs a literal index to name the component; an Arr peels to an element type at any index.
846 if (auto arr = type->isa<Arr>()) {
847 if (auto elem = peel(arr, *index)) val = assign_or_bail(elem, val);
848 } else if (lidx) {
849 val = assign_or_bail(type->proj(Lit::as(size), *lidx), val);
850 }
851 // The only `Idx 1` left is a mutable 1-tuple; d could be mut - that's why the tuple ctor is needed.
852 if (Lit::isa(size) == 1) return tuple(d, {val});
853
854 // insert((a, b, c, d), 2, x) -> (a, b, x, d)
855 if (auto t = d->isa<Tuple>(); t && lidx) {
856 auto new_ops = DefVec(t->ops().begin(), t->ops().end());
857 new_ops[*lidx] = val;
858 return tuple(type, new_ops);
859 }
860
861 // insert(‹4; x›, 2, y) -> (x, x, y, x)
862 if (auto a = pack_unrolls(d, *index, flags().scalarize_threshold)) {
863 auto new_ops = DefVec(*a, d->as<Pack>()->body());
864 new_ops[*lidx] = val;
865 return tuple(type, new_ops);
866 }
867
868 // `d#i ← (d#i)#js ← val` -> `d#(i, js) ← val`: the dual of the Extract fusion in World::extract1, and what
869 // keeps the read-modify-write chain a *single* write. The aggregate-rebuilding rules above win over it.
870 if (auto inner = is_frozen() ? nullptr : val->isa<Insert>())
871 if (auto ex = inner->tuple()->isa<Extract>(); ex && ex->tuple() == d && ex->index() == *index)
872 if (auto is = fused_index(d, *index, inner->index())) return insert(d, *is, inner->value());
873
874 // insert(d, index, d#index) -> d
875 if (auto ex = val->isa<Extract>())
876 if (ex->tuple() == d && ex->index() == *index) return d;
877
878 // insert(insert(x, index, y), index, val) -> insert(x, index, val)
879 if (auto insert = d->isa<Insert>()) {
880 if (insert->index() == *index) d = insert->tuple();
881 }
882
883 if (auto inhabitant = this->inhabitant(d->type())) return inhabitant;
884 return unify<Insert>(d, *index, val);
885}
886
887const Def* World::seq(bool is_pack, Shape shape, const Def* body) {
888 shape = shape.zonk();
889 body = body->zonk();
890
891 auto shape_ty = shape->unfold_type(); // nullptr for Level, which the diagnostic below reports
892 if (!shape_ty || !Shape::isa_extents(shape_ty))
893 shape->blame("expected a shape but got a value of type `{}`", type_of(*shape)).bail();
894
895 // `«1; T»` ≡ `T`, mirroring `[T]` ≡ `T`, so a literal size-1 axis folds out of the shape.
896 shape = shape.fold();
897 auto r = shape.rank();
898 if (r) {
899 if (*r == 0) return body;
900 // A literal-`0` extent empties everything below it: `«2, 0, 3; T»` is `«2; []»`, not `[]`.
901 for (nat_t i = 0; i != *r; ++i)
902 if (Shape::extent(shape[i]) == 0) return seq(is_pack, shape.take(i), prod(is_pack));
903 }
904
905 // `«a; «b; T»»` ≡ `«a, b; T»` - the very compression that already makes `«3; T»` out of `[T, T, T]`,
906 // one level up. A *mutable* body binds an index the fused shape could not express, so it stops fusion.
907 if (auto inner = body->isa_imm<Seq>(); inner && inner->is_intro() == is_pack)
908 if (auto fused = shape + inner->shape()) return seq(is_pack, fused, inner->body());
909
910 if (is_pack) return unify<Pack>(arr(shape, body->unfold_type()), *shape, body);
911 return unify<Arr>(body->unfold_type(), *shape, body);
912}
913
914const Def* World::seq(bool is_pack, Defs shape, const Def* body) {
915 if (shape.empty()) return body;
916 return seq(is_pack, tuple(shape), body);
917}
918
919const Lit* World::lit(const Def* type, u64 val) {
920 if (!type) return nullptr;
921 type = type->zonk();
922
923 if (auto size = Idx::isa(type)) {
924 if (auto s = Lit::isa(size)) {
925 if (*s != 0 && val >= *s) type->blame("index `{}` does not fit within arity `{}`", val, size).bail();
926 } else if (val != 0) { // 0 of any size is allowed
927 type->blame("cannot create literal `{}` of `Idx {}` as size is unknown", val, size).bail();
928 }
929 }
930
931 return unify<Lit>(type, val);
932}
933
934/*
935 * set
936 */
937
938template<bool Up>
939const Def* World::ext(const Def* type) {
940 type = type->zonk();
941
942 if (auto arr = type->isa<Arr>()) return pack(arr->shape(), ext<Up>(arr->body()));
943 if (auto sigma = type->isa<Sigma>())
944 return tuple(sigma, DefVec(sigma->ops(), [this](const Def* op) { return ext<Up>(op); }));
945 return unify<TExt<Up>>(type);
946}
947
948const Def* World::join(Defs ops_) {
949 auto ops = DefVec();
950 ops.reserve(ops_.size());
951 auto push = [&ops](const Def* op) {
952 if (!op->isa<Bot>()) ops.emplace_back(op);
953 };
954
955 for (auto op_ : ops_) {
956 auto op = op_->zonk();
957 // A nested Join is already flat, so one level suffices to keep the operation associative.
958 if (auto join = op->isa_imm<Join>())
959 for (auto o : join->ops())
960 push(o);
961 else
962 push(op);
963 }
964
965 auto kind = type(level(ops));
966 if (std::ranges::any_of(ops, [](const Def* op) { return op->isa<Top>(); })) return top(kind);
967
968 sort_unique(ops);
969
970 if (ops.empty()) return bot(kind);
971 if (ops.size() == 1) return ops[0];
972 return unify<Join>(kind, ops);
973}
974
975const Def* World::inj(const Def* type, const Def* value) {
976 type = type->zonk();
977 value = value->zonk();
978
979 if (type->isa<Variant>())
980 value->blame("injecting into variant type `{}` requires the index of a case", type).bail();
981 if (type->isa<Nom>()) return name(type, value);
982 if (auto join = type->isa<Join>()) {
983 auto value_t = value->unfold_type();
984 // Hole%s are resolved only once no case matches outright, so a mere search never commits to one.
985 auto is_case = [value, value_t](bool infer) {
986 auto alpha
987 = [infer](const Def* a, const Def* b) { return infer ? Check::alpha(a, b) : Check::test_alpha(a, b); };
988 return [=](const Def* c) {
989 // An open value may still turn out to be the singleton's inhabitant.
990 if (auto single = c->isa<Single>())
991 return alpha(single->op(), value)
992 || (!value->is_closed() && alpha(single->op()->unfold_type(), value_t));
993 return alpha(value_t, c);
994 };
995 };
996 if (!std::ranges::any_of(join->ops(), is_case(false)) && !std::ranges::any_of(join->ops(), is_case(true)))
997 value->blame("value of type `{}` is not a case of union type `{}`", type_of(value), type).bail();
998 return unify<Inj>(type, value, flags_t(0));
999 }
1000 if (auto v = Check::coerce(type, value)) return v;
1001 value->blame("value is not assignable to `{}`", type)
1002 .n("`{}` is neither a union nor a nominal type, so `inj` into it is the identity", type)
1003 .n("expected `{}`, got `{}`", type, type_of(value))
1004 .bail();
1005}
1006
1007const Def* World::inj(const Def* type, nat_t index, const Def* value) {
1008 type = type->zonk();
1009 value = value->zonk();
1010
1011 if (auto nom = type->isa<Nom>()) return unify<Name>(nom, inj(nom->op(), index, value));
1012 auto variant = type->isa<Variant>();
1013 if (!variant) value->blame("cannot inject into case {} of non-variant type `{}`", index, type).bail();
1014 if (index >= variant->num_ops())
1015 value->blame("variant type `{}` has {} cases but case {} was requested", type, variant->num_ops(), index)
1016 .bail();
1017 if (auto v = Check::coerce(variant->op(index), value)) return unify<Inj>(type, v, flags_t(index));
1018 value->blame("value is not assignable to case {} of variant type `{}`", index, type)
1019 .n("expected `{}`, got `{}`", variant->op(index), type_of(value))
1020 .bail();
1021}
1022
1024 auto zops = Def::zonk(ops);
1025 auto kind = Variant::infer(*this, zops);
1026 if (zops.empty()) return bot(kind);
1027 return unify<Variant>(kind, zops);
1028}
1029
1030const Def* World::match(Defs ops_) {
1031 auto ops = Def::zonk(ops_);
1032 if (ops.size() == 1) return ops.front();
1033
1034 auto scrutinee = ops.front();
1035 auto arms = ops.span().subspan(1);
1036
1037 for (auto arm : arms)
1038 if (!arm->isa_type<Pi>())
1039 arm->blame("arm of a match expression does not have a function type but has type `{}`", type_of(arm))
1040 .bail();
1041
1042 auto scrut_t = scrutinee->unfold_type();
1043 if (!scrut_t) scrutinee->blame("scrutinee of a match expression has no type").bail();
1044
1045 if (auto variant = scrut_t->isa<Variant>()) {
1046 if (arms.size() != variant->num_ops())
1047 scrutinee
1048 ->blame("match expression has {} arms but variant type has {} cases", arms.size(), variant->num_ops())
1049 .bail();
1050
1051 const Def* type = nullptr;
1052 for (size_t i = 0, e = arms.size(); i != e; ++i) {
1053 auto pi = arms[i]->isa_type<Pi>();
1054 if (!Check::alpha(pi->dom(), variant->op(i)))
1055 arms[i]
1056 ->blame("domain type `{}` of match-expression arm does not match variant case `{}`", pi->dom(),
1057 variant->op(i))
1058 .bail();
1059 type = type ? this->join({type, pi->codom()}) : pi->codom();
1060 }
1061
1062 if (auto inj = scrutinee->isa<Inj>()) return app(arms[inj->index()], inj->value());
1063 return unify_elim<Match>(type, ops);
1064 }
1065
1066 auto join = scrut_t->isa<Join>();
1067
1068 // A scrutinee that is not a union is the degenerate one-case union; cf. `d#i` for `i: Idx 1`.
1069 auto cases = Match::cases(scrutinee);
1070
1071 // The first accepting arm handles a case, any later one is dead; arms are *not* sorted, their order is the
1072 // tie-break.
1073 auto sel = DefVec(cases.size());
1074 for (size_t i = 0, e = cases.size(); i != e; ++i) {
1075 auto c = cases[i];
1076 auto j = std::ranges::find_if(arms, [c](const Def* arm) { return Match::accepts(arm, c); });
1077 // Hole%s are resolved only once no arm matches outright, so a mere search never commits to one.
1078 if (j == arms.end())
1079 j = std::ranges::find_if(arms, [c](const Def* arm) { return Match::accepts(arm, c, true); });
1080 if (j == arms.end()) scrutinee->blame("match expression has no arm for union case `{}`", c).bail();
1081 sel[i] = *j;
1082 }
1083
1084 // Keep the arms actually selected, in the order written; a dead arm is dropped.
1085 auto res = DefVec();
1086 const Def* type = nullptr;
1087 res.emplace_back(scrutinee);
1088 for (auto arm : arms) {
1089 if (!std::ranges::contains(sel, arm)) continue;
1090 auto codom = arm->isa_type<Pi>()->codom();
1091 type = type ? this->join({type, codom}) : codom;
1092 res.emplace_back(arm);
1093 }
1094
1095 // An arm covering several cases expects the wider union, so narrow values are injected back into it.
1096 auto dispatch
1097 = [this](const Def* arm, const Def* value) { return app(arm, inj(arm->isa_type<Pi>()->dom(), value)); };
1098
1099 if (!join) return dispatch(sel[0], scrutinee);
1100
1101 // A constructor fixes the active union case. Dispatch before the Match can
1102 // escape into later lowering phases, where the payload representation may
1103 // already have changed (for example, a tensor may have become a buffer).
1104 if (auto inj = scrutinee->isa<Inj>()) {
1105 auto value = inj->value();
1106 for (size_t i = 0, e = cases.size(); i != e; ++i)
1107 if (Check::alpha(value->unfold_type(), cases[i])) return dispatch(sel[i], value);
1108 scrutinee->blame("injected value type `{}` is not a case of union type `{}`", type_of(value), join).bail();
1109 }
1110
1111 return unify_elim<Match>(type, res);
1112}
1113
1114const Def* World::single(const Def* op) {
1115 op = op->zonk();
1116 if (auto t = op->unfold_type())
1117 if (auto tt = t->unfold_type()) return unify<Single>(tt, op);
1118 op->blame("operand is too high in the universe hierarchy to inhabit a singleton type")
1119 .n("a singleton type sits one level above its inhabitant, and nothing sits above `Level`")
1120 .bail();
1121}
1122
1123const Def* World::wrap(const Def* op) {
1124 op = op->zonk();
1125 return unify<Wrap>(single(op), op);
1126}
1127
1128const Def* World::unwrap(const Def* op) {
1129 op = op->zonk();
1130 if (auto single = op->isa_type<Single>()) return single->op();
1131 op->blame("operand of a singleton elimination is of type `{}` but must be of singleton type", type_of(op)).bail();
1132}
1133
1134const Nom* World::nom(flags_t key, const Def* type) {
1135 type = type->zonk();
1136 if (auto t = type->unfold_type()) return unify<Nom>(t, type, key);
1137 type->blame("`Level` cannot be wrapped in a nominal newtype").bail();
1138}
1139
1140const Def* World::name(const Def* n, const Def* value) {
1141 auto nom = n->zonk()->isa<Nom>();
1142 value = value->zonk();
1143 if (!nom) value->blame("cannot inject into non-nominal type `{}`", n).bail();
1144 if (auto v = Check::coerce(nom->op(), value)) return unify<Name>(nom, v);
1145 value->blame("value is not assignable to nominal type `{}`", nom)
1146 .n("expected `{}`, got `{}`", nom->op(), type_of(value))
1147 .bail();
1148}
1149
1150const Def* World::struc(const Def* value) {
1151 value = value->zonk();
1152 if (auto name = value->isa<Name>()) return name->op();
1153 if (auto nom = value->isa_type<Nom>()) return unify_elim<Struc>(nom->op(), value);
1154 value->blame("operand of a nominal elimination is of type `{}` but must be of nominal type", type_of(value)).bail();
1155}
1156
1157Sym World::append_suffix(Sym symbol, std::string suffix) {
1158 auto name = symbol.str();
1159 if (auto pos = name.rfind(suffix); pos != std::string::npos) {
1160 auto end = pos + suffix.size();
1161 auto num = std::string_view(name).substr(end);
1162 if (num.empty()) return sym(name + "_1");
1163 if (num.size() > 1 && num[0] == '_'
1164 && std::ranges::all_of(num.substr(1), [](char c) { return std::isdigit((unsigned char)c); }))
1165 return sym(name.substr(0, end) + "_" + std::to_string(std::stoull(std::string(num.substr(1))) + 1));
1166 }
1167 return sym(name + suffix);
1168}
1169
1170Defs World::reduce(const Var* var, const Def* arg) {
1171 auto mut = var->binder();
1172 auto off = mut->reduction_offset();
1173 auto n = mut->num_ops() - off;
1174 auto ops = Defs(mut->ops().begin() + off, n);
1175 // `[var -> var]` is the identity and so is a substitution without an occurrence.
1176 if (var == arg || std::ranges::none_of(ops, [var](const Def* op) { return op->has_free_var(var); })) return ops;
1177
1178 auto reduct = this->reduct(var, arg);
1179 auto rw = VarRewriter(var, arg); // one rewriter for all slots: they share their sub-rewrites
1180 for (size_t i = 0; i != n; ++i) {
1181 auto& slot = reduct->ops()[i];
1182 assert(slot != Filling && "op requires its own reduction");
1183 if (slot) continue;
1184 slot = Filling;
1185 slot = rw.rewrite(ops[i]);
1186 }
1187
1188 return reduct->ops();
1189}
1190
1191const Def* World::cached_reduct(const Var* var, const Def* arg, size_t i) {
1192 auto mut = var->binder();
1193 auto op = mut->op(i + mut->reduction_offset());
1194 if (var == arg || (op && !op->has_free_var(var))) return op;
1195
1196 if (auto it = move_.substs.find(std::pair{var, arg}); it != move_.substs.end())
1197 if (auto slot = it->second->ops()[i]; slot != Filling) return slot;
1198 return nullptr;
1199}
1200
1201const Def* World::reduce(const Var* var, const Def* arg, size_t i) {
1202 auto mut = var->binder();
1203 auto off = mut->reduction_offset();
1204 auto op = mut->op(i + off);
1205 if (var == arg) return op; // `[var -> var]` is the identity
1206 if (!op) fe::throwf("cannot reduce `{}`: operand {} is not set", mut, i + off);
1207 if (!op->has_free_var(var)) return op; // no occurrence: no Reduct, no VarRewriter
1208
1209 auto& slot = reduct(var, arg)->ops()[i];
1210 assert(slot != Filling && "op requires its own reduction");
1211 if (!slot) {
1212 slot = Filling;
1213 // If this ever bites performance, share one VarRewriter per Reduct and free it once all its slots are filled.
1214 slot = VarRewriter(var, arg).rewrite(op);
1215 }
1216
1217 return slot;
1218}
1219
1220void World::for_each(bool elide_empty, std::function<void(Def*)> f, bool schedule /* = false */) {
1221 auto keep = [elide_empty](Def* mut) { return mut->is_closed() && (!elide_empty || mut->is_set()); };
1222 auto muts = fe::Vector<Def*>();
1223
1224 if (schedule) {
1225 auto done = MutSet();
1226 for (auto mut : externals().muts())
1227 post_order(mut, done, muts, [](Def*) { return true; }, keep);
1228 } else {
1229 auto queue = fe::BFSWorklist<MutSet>();
1230 for (auto mut : externals().muts())
1231 queue.push(mut);
1232
1233 while (!queue.empty()) {
1234 auto mut = queue.pop();
1235 if (keep(mut)) muts.emplace_back(mut);
1236
1237 for (auto op : mut->deps())
1238 for (auto local_mut : op->local_muts())
1239 queue.push(local_mut);
1240 }
1241 }
1242
1243 for (auto mut : muts)
1244 f(mut);
1245}
1246
1247/*
1248 * debugging
1249 */
1250
1251#ifdef MIM_ENABLE_CHECKS
1252
1253void World::breakpoint(u32 gid) { state_.breakpoints.emplace(gid); }
1254void World::watchpoint(u32 gid) { state_.watchpoints.emplace(gid); }
1255
1257 auto i = std::ranges::find_if(move_.sea, [=](auto def) { return def->gid() == gid; });
1258 if (i == move_.sea.end()) return nullptr;
1259 return *i;
1260}
1261
1263 for (auto mut : externals().muts())
1264 assert(mut->is_closed() && mut->is_set());
1265 for (auto anx : annexes().defs())
1266 assert(anx->is_closed());
1267 assert(move_.levels.empty() && "postponed universe constraints must be solved via solve_levels");
1268 return *this;
1269}
1270
1271#endif
1272
1273#ifndef DOXYGEN
1274template const Def* World::ext<true>(const Def*);
1275template const Def* World::ext<false>(const Def*);
1276template const Def* World::app<true>(const Def*, const Def*);
1277template const Def* World::app<false>(const Def*, const Def*);
1278template const Def* World::implicit_app<true>(const Def*, const Def*);
1279template const Def* World::implicit_app<false>(const Def*, const Def*);
1280#endif
1281
1282// Interning here - once per push - instead of in unify() keeps ~170k redundant Driver::dbg lookups per compile
1283// off the hot path: only a few thousand distinct Loc%s occur, yet every emitted Def wants one.
1284// Restore rolls both fields back together, so popping a scope never re-interns either.
1286 auto& curr = state_.pod.curr_loc;
1287 if (loc == curr.loc) return ScopedLoc(curr); // nested emitters push the same Loc; don't re-intern it
1288 return ScopedLoc(curr, {loc, loc ? driver().dbg(Dbg(loc)) : DbgKey()});
1289}
1290
1291} // namespace mim
A (possibly paramterized) Array.
Definition tuple.h:305
Arr * set_body(const Def *body)
Definition tuple.h:318
Arr * set_shape(const Def *shape)
Definition tuple.h:317
Definition axm.h:9
static std::tuple< const Axm *, u8, u8 > next(const Def *callee)
Like Axm::get, but advances the counter as one more App is about to be built on top of callee.
Definition axm.cpp:44
NormalizeFn normalizer() const
Definition axm.h:36
static const Def * is_uniform(Defs defs)
Yields defs.front(), if all defs are Check::alpha-equivalent (Mode::Test) and nullptr otherwise.
Definition check.cpp:120
static const Def * coerce(const Def *type, const Def *value)
Can value be assigned to sth of type?
Definition check.h:105
static bool test_alpha(const Def *d1, const Def *d2)
Definition check.h:101
static bool alpha(const Def *d1, const Def *d2)
Definition check.h:93
Base class for all Defs.
Definition def.h:289
bool is_set() const
Definition def.h:396
const Def * zonk_mut() const
If mutable, zonk()s all ops and tries to immutabilize it; otherwise just zonk.
Definition check.cpp:26
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.
Definition def.cpp:647
constexpr Node node() const noexcept
Definition def.h:315
const Def * zonk() const
If Holes have been filled, reconstruct the program without them.
Definition check.cpp:20
bool is_intro() const noexcept
Definition def.h:347
constexpr auto ops() const noexcept
Definition def.h:374
size_t reduction_offset() const noexcept
First Def::op that needs to be dealt with during reduction; e.g.
Definition def.cpp:597
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:588
const Def * op(size_t i) const noexcept
Definition def.h:377
const Def * unfold_type() const
Yields the type of this Def and builds a new Type (LSucc n) if necessary.
Definition def.cpp:469
bool has_free_var(const Var *) const
Same as free_vars().contains(var).
Definition def.cpp:277
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.
Definition def.h:333
bool is_external() const noexcept
Definition def.h:569
Loc loc() const
Definition def.h:632
Sym sym() const
Definition def.h:633
const Def * var_type()
If this is a binder, compute the type of its Variable.
Definition def.cpp:239
constexpr auto reduce(const Def *arg) const
Definition def.h:681
const T * isa_imm() const
Definition def.h:582
bool is_closed() const
Same as !has_free_vars().
Definition def.cpp:357
const Var * has_var()
Only returns not nullptr, if Var of this mutable has ever been created.
Definition def.h:496
const T * isa_type() const
Is Def::unfold_type a T? Yields nullptr for Level, which has no type at all.
Definition def.h:363
constexpr size_t num_ops() const noexcept
Definition def.h:378
Some "global" variables needed all over the place.
Definition driver.h:64
Flags & flags()
Definition driver.h:77
fe::Log & log()
Definition driver.h:79
Extracts from a Sigma or Array-typed Extract::tuple the element at position Extract::index.
Definition tuple.h:360
const Def * tuple() const
Definition tuple.h:370
static Hole * isa_unset(const Def *def)
Definition check.h:53
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
Definition def.cpp:670
static std::optional< nat_t > isa_lit(const Def *def)
Definition def.cpp:677
Constructs a Join or Variant value.
Definition union.h:60
Creates a new Tuple / Pack by inserting Insert::value at position Insert::index into Insert::tuple.
Definition tuple.h:385
A union type whose ops are sorted and deduplicated: T ∪ T is T.
Definition union.h:44
A function.
Definition lam.h:91
const Def * body() const
Definition lam.h:104
static std::optional< T > isa(const Def *def)
Definition def.h:976
static T as(const Def *def)
Definition def.h:982
static bool accepts(const Def *arm, const Def *c, bool infer=false)
Does arm handle c?
Definition union.cpp:18
static DefVec cases(const Def *scrutinee)
Definition union.cpp:10
Nominal newtype term introduction.
Definition nom.h:34
Nominal newtype formation.
Definition nom.h:11
const Def * op() const
The wrapped type.
Definition nom.h:21
A (possibly paramterized) Tuple.
Definition tuple.h:332
A dependent function type.
Definition lam.h:14
const Def * dom() const
Definition lam.h:38
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
const Def * reduce(const Def *arg) const
Definition lam.h:79
Base class for Arr and Pack.
Definition tuple.h:267
const Def * body() const
Definition tuple.h:280
Shape shape() const
Definition tuple.h:279
const Def * reduce(const Def *arg) const
Definition tuple.h:298
The extents of a Seq's axes: a plain Nat for a single axis, an aggregate of them for several.
Definition tuple.h:194
std::optional< nat_t > rank() const
Number of axes; std::nullopt if not statically known.
Definition tuple.cpp:23
bool is_fused() const
Does this span several axes? Also true for a dynamic rank.
Definition tuple.h:211
static std::optional< nat_t > extent(const Def *axis)
The extent of axis: the axis itself for a shape, its Idx size for an index.
Definition tuple.cpp:33
bool is_dim() const
Is this a single axis - a Nat/Idx rather than an aggregate of them?
Definition tuple.cpp:16
static bool isa_extents(const Def *)
Is type Nat - or an aggregate of Nats, i.e. a shape?
Definition tuple.cpp:46
Shape drop(nat_t n) const
All but the leading n axes.
Definition tuple.cpp:61
Shape take(nat_t n) const
The leading n axes.
Definition tuple.h:236
constexpr Shape() noexcept=default
static bool isa_indices(const Def *)
Is type Idx - or an aggregate of Idxs, i.e. a fused index?
Definition tuple.cpp:50
A dependent tuple type.
Definition tuple.h:39
static const Def * infer(World &, Defs)
Definition check.cpp:492
Singleton type formation.
Definition single.h:10
const Def * op() const
Definition single.h:20
Data constructor for a Sigma.
Definition tuple.h:84
static const Def * infer(World &, Defs)
Definition check.cpp:488
VarRewriter(World &world)
Definition rewrite.h:146
A variable introduced by a binder (mutable).
Definition def.h:857
Def * binder() const
The binder of this Var.
Definition def.h:867
A sum type whose cases are positional: unlike a Join, they are neither sorted nor deduplicated.
Definition union.h:86
static const Def * infer(World &, Defs)
Definition check.cpp:494
static const Def * isa_lone(const Def *d)
The payload A of a single-case Variant | A, which is isomorphic to A.
Definition union.h:109
Driver & driver()
Definition world.h:232
const flags_t * flags(const Def *d) const
The flags of the annex def, if it is one.
Definition world.h:243
void attach_alias(flags_t, Sym)
Registers a further Sym for an already attach()ed annex, sharing its flags_t;.
Definition world.cpp:149
const Def * attach(flags_t, Sym, const Def *)
Definition world.cpp:137
void internalize(Def *)
Definition world.cpp:130
void externalize(Def *)
Definition world.cpp:123
const Lit * lit_idx(nat_t size, u64 val)
Constructs a Lit of type Idx of size size.
Definition world.h:576
const Def * insert(const Def *d, const Def *i, const Def *val)
Definition world.cpp:802
const Def * variant(Defs ops)
Definition world.cpp:1023
const Lit * lit(const Def *type, u64 val)
Definition world.cpp:919
const Def * inhabitant(const Def *type)
The sole inhabitant of type, if it has exactly one, nullptr otherwise.
Definition world.h:342
const Def * struc(const Def *nom)
Get the structural part of a nominal.
Definition world.cpp:1150
auto & muts()
Definition world.h:715
const Def * pack(Shape shape, const Def *body)
Definition world.h:484
const Def * level(Defs types)
The lmax of the levels of all types, each of which must be a Type level.
Definition world.cpp:349
const Lit * lit_i8()
Definition world.h:570
World(Driver *, Sym name)
Definition world.cpp:208
void watchpoint(u32 gid)
Trigger breakpoint in your debugger when Def::setting a Def with this gid.
Definition world.cpp:1254
Driver & driver()
Definition world.h:107
const Type * type(const Def *level)
Definition world.cpp:235
const Def * component(const Def *type, Defs parts, size_t n, size_t i)
The type of component i of the n-ary type, given its leading components parts.
Definition world.cpp:585
const Def * lsucc(const Def *op, level_t offset=1)
Definition world.cpp:247
const Lit * lit_level_1()
Definition world.h:558
const Lit * lit_tt()
Definition world.h:597
const Def * sigma(Defs ops)
Definition world.cpp:506
const Def * app(const Def *callee, const Def *arg)
Definition world.cpp:416
const Def * match(Defs)
Definition world.cpp:1030
const Def * arr(Shape shape, const Def *body)
Definition world.h:483
const Def * seq(bool is_pack, Shape shape, const Def *body)
Definition world.cpp:887
const Pi * pi(const Def *dom, const Def *codom, bool implicit=false)
Definition world.h:386
ScopedLoc push(Loc)
Definition world.cpp:1285
const Def * implicit_holes(const Def *callee)
Only places the Holes.
Definition world.cpp:401
World & verify()
Verifies that all externals() and annexes() are Def::is_closed(), if MIM_ENABLE_CHECKS.
Definition world.cpp:1262
const Def * bot(const Def *type)
Definition world.h:604
const Def * single(const Def *)
Singleton type formation.
Definition world.cpp:1114
const Idx * type_idx()
Definition world.h:623
Sym name() const
Definition world.h:113
const fe::Log & log() const
Log via log().e("...", args) etc.; owned by the Driver.
Definition world.cpp:224
bool is_lenient() const
Definition world.h:167
const Def * fuse(Seq *seq)
«i: a; «j: b; T»» -> «v: (a, b); T» - the inverse of World::peel, for a nest that World::seq cannot f...
Definition world.cpp:660
const Def * annex(Sym sym)
Lookup annex by Sym.
Definition world.cpp:160
const Nat * type_nat()
Definition world.h:622
void for_each(bool elide_empty, std::function< void(Def *)>, bool schedule=false)
Definition world.cpp:1220
Hole * mut_hole(const Def *type)
Definition world.h:361
const Lam * lam(const Pi *pi, Lam::Filter f, const Def *body)
Definition world.h:414
const Def * tuple(Defs ops)
Definition world.cpp:516
const Def * gid2def(u32 gid)
Lookup Def by gid.
Definition world.cpp:1256
const Def * lmax(Defs levels)
Definition world.cpp:260
Flags & flags()
Retrieve compile Flags.
Definition world.cpp:225
const Def * implicit_app(const Def *callee, const Def *arg)
Definition world.cpp:411
Annexes & annexes()
Definition world.h:289
const Def * unwrap(const Def *)
Singleton term elimination.
Definition world.cpp:1128
const Def * inj(const Def *type, const Def *value)
Definition world.cpp:975
const Type * type()
Definition world.h:332
const Axm * axm(NormalizeFn n, u8 curry, u8 trip, const Def *type, plugin_t p, tag_t t, sub_t s)
Definition world.h:368
const Lit * lit_level_0()
Definition world.h:557
fe::Error & error()
Definition world.cpp:222
void solve_levels()
Defaults the level Holes of all postponed constraints that are still unknown to 0 and checks them.
Definition world.cpp:313
const Def * extract(const Def *d, const Def *i)
Definition world.cpp:598
bool is_frozen() const
Definition world.h:151
Sym sym(std::string_view)
Definition world.cpp:228
const Lit * lit_ff()
Definition world.h:596
fe::Restore< CurrLoc > ScopedLoc
Definition world.h:129
const Def * join(Defs ops)
Definition world.cpp:948
const Def * ext(const Def *type)
Definition world.cpp:939
Sym append_suffix(Sym name, std::string suffix)
Appends a suffix or an increasing number if the suffix already exists.
Definition world.cpp:1157
static constexpr nat_t Num_Lit_Nats
Def::arity of a Sigma is lit_nat(num_ops()) and Def::num_projs reads it straight back out,...
Definition world.h:561
const Def * var(Def *mut)
Definition world.cpp:392
const Tuple * tuple()
the unit value of type []
Definition world.h:530
const Nom * nom(flags_t key, const Def *type)
Make type Nominal under key.
Definition world.cpp:1134
const Def * raw_app(const Axm *axm, u8 curry, u8 trip, const Def *type, const Def *callee, const Def *arg)
Definition world.cpp:501
const Def * prod(bool term, Defs ops)
Definition world.h:497
const Externals & externals() const
Definition world.h:286
const Def * peel(const Seq *seq, const Def *index)
seq with the axes covered by index peeled off - the element itself once index covers all of them.
Definition world.cpp:646
const Def * type_indices(Shape shape)
The type of an index into shape: Idx n, a Sigma of those, or «i: r; Idx (s#i)» for a dynamic rank.
Definition world.cpp:678
const Sigma * sigma()
The unit type within Type 0.
Definition world.h:471
const Lit * lit_nat(nat_t a)
Definition world.h:563
const State & state() const
Definition world.h:106
const Def * wrap(const Def *)
Singleton term introduction.
Definition world.cpp:1123
const Def * drop(const Seq *s, nat_t k)
s without its leading k axes - its body once k covers all of them; nullptr if that isn't a type.
Definition world.cpp:637
const Def * top(const Def *type)
Definition world.h:605
Arr * mut_arr()
Definition world.h:478
const Lit * lit_level(u64 level)
Definition world.h:554
Defs reduce(const Var *var, const Def *arg)
Yields the new body of [mut->var() -> arg]mut.
Definition world.cpp:1170
void breakpoint(u32 gid)
Trigger breakpoint in your debugger when creating a Def with this gid.
Definition world.cpp:1253
const Def * annex()
Get Axm from a plugin.
Definition world.h:309
const Level * type_level()
Definition world.h:319
Definition ast.h:16
u64 nat_t
Definition types.h:37
static const Def * level_of(const Def *type)
Definition world.cpp:336
GIDSet< Lam * > LamSet
Definition lam.h:211
u64 flags_t
Definition types.h:39
static level_t eval0(const Def *level)
Definition world.cpp:299
fe::View< const Def * > Defs
Definition def.h:96
void post_order(Def *mut, MutSet &done, fe::Vector< Def * > &res, MutPred auto descend, MutPred auto collect)
Appends the mutables reachable from mut via Def::deps / Def::local_muts to res in post-order: callees...
Definition def.h:1155
u64 level_t
Definition types.h:36
TExt< true > Top
Definition union.h:40
GIDSet< Def * > MutSet
Definition def.h:106
fe::Vector< const Def * > DefVec
Definition def.h:98
uint32_t u32
Definition types.h:27
TExt< false > Bot
Definition union.h:39
uint64_t u64
Definition types.h:27
auto type_of(const Def *def)
Def::unfold_type of def for a diagnostic - Level is the one Def that has no type at all.
Definition def.h:1144
uint8_t u8
Definition types.h:27
static void default_level(const Def *level)
Definition world.cpp:288
@ Arr
Definition def.h:125
@ Variant
Definition def.h:125
@ Single
Definition def.h:125
@ Sigma
Definition def.h:125
static std::string demangle(plugin_t plugin)
Reverts an Axm::mangled plugin back to its name; never longer than Annex::Max_Plugin_Size.
Definition plugin.cpp:29
Compiler switches that must be saved and looked up in later phases of compilation.
Definition flags.h:11