MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
check.cpp
Go to the documentation of this file.
1#include "mim/check.h"
2
3#include <fe/assert.h>
4
5#include "mim/driver.h"
6#include "mim/rewrite.h"
7#include "mim/rule.h"
8#include "mim/world.h"
9
10namespace mim {
11
12bool Def::needs_zonk() const {
13 if (has_dep(Dep::Hole)) {
14 for (auto mut : local_muts())
15 if (Hole::isa_set(mut)) return true;
16 }
17
18 return false;
19}
20
21const Def* Def::zonk() const {
22 // A Hole needs special care: even when it is still unset, its *type* may have to be refreshed; see zonk_mut.
23 if (isa_mut<Hole>()) return zonk_mut();
24 return needs_zonk() ? world().zonker().rewrite(this) : this;
25}
26
27const Def* Def::zonk_mut() const {
28 if (auto hole = isa_mut<Hole>()) {
29 auto [last, op] = hole->find();
30 if (op) return op->zonk();
31 // The Hole is still unset, but its *type* may mention Hole%s that have been resolved in the meantime.
32 // Refresh it; otherwise e.g. an `«?n; T»` type won't collapse to `T` after `?n` has been unified with `1`.
33 if (auto t = last->type())
34 if (auto new_t = t->zonk_mut(); new_t != t) last->set_type(new_t);
35 return last;
36 }
37
38 if (!is_set()) return this;
39
40 if (auto mut = isa_mut()) {
41 for (auto def : deps())
42 if (def->needs_zonk()) return world().zonker().rewire_mut(mut);
43
44 if (auto imm = mut->immutabilize()) return imm;
45 return this;
46 }
47
48 return zonk();
49}
50
52 return DefVec(defs, [](const Def* def) { return def->zonk(); });
53}
54
55/*
56 * Hole
57 */
58
59std::pair<Hole*, const Def*> Hole::find() {
60 auto def = Def::op(0);
61 auto last = this;
62
63 while (def) {
64 auto h = def->isa_mut<Hole>();
65 if (!h) break;
66 def = h->op();
67 last = h;
68 }
69
70 auto root = def ? def : last;
71
72 // path compression
73 for (auto h = this; h != last;) {
74 auto next = h->op()->as_mut<Hole>();
75 h->set(root);
76 h = next;
77 }
78
79 return {last, def};
80}
81
82const Def* Hole::tuplefy(nat_t n) {
83 if (is_set()) return this;
84
85 auto& w = world();
86 auto holes = DefVec(n);
87 if (auto [sigma, var] = type()->isa_binder<Sigma>(); sigma && n >= 1) {
88 auto rw = VarRewriter(var, this);
89 holes[0] = w.mut_hole(sigma->op(0));
90 for (size_t i = 1; i != n; ++i) {
91 rw.map(sigma->var(n, i - 1), holes[i - 1]);
92 holes[i] = w.mut_hole(rw.rewrite(sigma->op(i)));
93 }
94 } else {
95 for (size_t i = 0; i != n; ++i)
96 holes[i] = w.mut_hole(type()->proj(n, i));
97 }
98
99 auto tuple = w.tuple(holes);
100 set(tuple);
101 return tuple;
102}
103
104/*
105 * Checker
106 */
107
108#ifdef MIM_ENABLE_CHECKS
109template<Checker::Mode mode>
110bool Checker::fail() {
111 if (mode == Check && world().flags().break_on_alpha) fe::breakpoint();
112 return false;
113}
114
115const Def* Checker::fail() {
116 if (world().flags().break_on_alpha) fe::breakpoint();
117 return {};
118}
119#endif
120
122 if (defs.empty()) return nullptr;
123 auto first = defs.front();
124 auto same = [first](const Def* def) { return alpha<Test>(first, def); };
125 return std::ranges::all_of(defs.subspan(1), same) ? first : nullptr;
126}
127
128const Def* Checker::assignable_(const Def* type, const Def* val) {
129 auto val_t = val->unfold_type();
130 if (!val_t) return fail(); // Univ has no type, so it is assignable to nothing
131 auto val_ty = val_t->zonk();
132 if (type == val_ty) return val;
133
134 auto& w = world();
135
136 // Implicit insertion at a coercion site: @p val expects implicit arguments, but @p type is an
137 // *explicit* function type. Fill them with Hole%s, as World::implicit_app does at application sites.
138 // This lets polymorphic functions be passed as arguments, e.g. `affine.id` to a parameter of type
139 // `«r; affine.Index» → «r; affine.Index»`, without writing `affine.id @r`.
140 // Only do this when @p type is a Pi: if it is a Hole, we'd commit before knowing what's expected;
141 // if it's an aggregate, we might consume implicits belonging to the value's element type.
142 if (auto pi = type->isa<Pi>(); pi && !pi->is_implicit() && Pi::isa_implicit(val_ty)) {
143 while (auto ipi = Pi::isa_implicit(val_ty)) {
144 val = w.app(val, w.mut_hole(ipi->dom()));
145 val_ty = val->unfold_type()->zonk();
146 }
147 if (type == val_ty) return val;
148 }
149
150 if (auto sigma = type->isa<Sigma>()) {
151 if (!alpha_<Check>(type->arity(), val_ty->arity())) return fail();
152
153 size_t a = sigma->num_ops();
154 auto red = sigma->reduce(val);
155 auto new_ops = DefVec(a);
156 for (size_t i = 0; i != a; ++i) {
157 auto new_val = assignable_(red[i], val->proj(a, i));
158 if (!new_val) return fail();
159 new_ops[i] = new_val;
160 }
161 return w.tuple(new_ops);
162 }
163
164 if (auto uniq = val_ty->isa<Uniq>()) {
165 if (auto new_val = assignable(type, uniq->op())) return new_val;
166 return fail();
167 }
168
169 return alpha_<Check>(type, val_ty) ? val : fail();
170}
171
172std::pair<Checker::Binders::iterator, bool> Checker::bind(Def* mut, const Def* d) {
173 if (!mut) return {binders_.end(), true};
174
175 auto res = binders_.emplace(mut, d);
176 if (res.second) {
177 // A new binding may change how bound Var%s compare, so positive memo entries may become invalid.
178 for (auto& memo : memo_)
179 memo.clear();
180 // A Var that has never been created cannot occur in any Def.
181 if (auto var = mut->has_var()) bound_ = world().vars().insert(bound_, var);
182 }
183
184 return res;
185}
186
187/// Is @p def a Seq that spans exactly one dimension, i.e. one that a rank can be peeled off?
188static bool isa_dim(const Def* def) {
189 auto seq = def->isa<Seq>();
190 return seq && seq->arity()->unfold_type()->zonk_mut()->isa<Nat>();
191}
192
193/// The rank of `«s; T»` with `s: «r; Nat»` is unknown as long as `r` is: World::seq cannot un-nest it yet.
194/// @returns the unset Hole standing for `r`, or `nullptr`.
195static Hole* isa_flex_rank(const Def* def) {
196 if (auto seq = def->isa_imm<Seq>()) {
197 if (auto shape = Hole::isa_unset(seq->arity()->zonk_mut())) {
198 if (auto arr = shape->type()->zonk_mut()->isa<Arr>()) return Hole::isa_unset(arr->arity()->zonk_mut());
199 }
200 }
201 return nullptr;
202}
203
204// These may be α-equivalent to a Def with a different Node or Def::flags(); see alpha_impl_.
205static bool is_flex(const Def* def) {
206 auto n = def->node();
207 return n == Node::Hole || n == Node::Top || n == Node::UMax || Prod::isa_node(n) || Seq::isa_node(n);
208}
209
210template<Checker::Mode mode>
211std::optional<bool> Checker::try_alpha_(const Def* d1, const Def* d2) {
212 // Pointer equality decides the matter, unless a free Var of an immutable is bound on one side only: λx.x vs λz.x.
213 if (d1 == d2 && (d1->isa_mut() || bound_.empty() || !d1->has_free_vars_in(bound_))) return true;
214
215 // Only a ground Def is stable under Def::zonk_mut, which rewires mutables in place and unifies Hole%s.
216 if ((d1->node() != d2->node() || d1->flags() != d2->flags()) && d1->is_ground() && d2->is_ground() && !is_flex(d1)
217 && !is_flex(d2))
218 return fail<mode>();
219
220 return {};
221}
222
223template<Checker::Mode mode>
224bool Checker::alpha_(const Def* d1, const Def* d2) {
225 if (auto res = try_alpha_<mode>(d1, d2)) return *res;
226
227 auto& memo = memo_[mode];
228 auto key = memo_key(d1, d2);
229 if (memo.contains(key)) return true;
230 if (!alpha_impl_<mode>(d1, d2)) return false;
231 memo.emplace(key);
232 return true;
233}
234
235template<Checker::Mode mode>
236bool Checker::alpha_impl_(const Def* d1, const Def* d2) {
237 for (bool todo = true; todo;) {
238 // below we check type and arity which may in turn open up more opportunities for zonking
239 todo = false;
240 d1 = d1->zonk_mut();
241 d2 = d2->zonk_mut();
242
243 if (auto res = try_alpha_<mode>(d1, d2); res.has_value()) return *res;
244
245 auto h1 = d1->isa_mut<Hole>();
246 auto h2 = d2->isa_mut<Hole>();
247
248 if constexpr (mode == Check) {
249 if (h1) return check(h1, d2);
250 if (h2) return check(h2, d1);
251 } else if (h1 || h2) // mode == Test and h1 or h2 is an unresolved Hole
252 return fail<Test>();
253
254 if (!d1->is_set() || !d2->is_set()) return fail<mode>();
255
256 auto mut1 = d1->isa_mut();
257 auto mut2 = d2->isa_mut();
258
259 if (mut1 && mut2 && mut1 == mut2) return true;
260
261 // Globals are HACKs and require additionaly HACKs:
262 // Unless they are pointer equal (above) always consider them unequal.
263 if (d1->isa<Global>() || d2->isa<Global>()) return false;
264
265 if (auto [i, ins] = bind(mut1, d2); !ins) return i->second == d2;
266 if (auto [i, ins] = bind(mut2, d1); !ins) return i->second == d1;
267
268 if (d1->isa<Top>() || d2->isa<Top>()) return mode == Check;
269
270 auto t1 = d1->type();
271 auto t2 = d2->type();
272 if (t1 && t2 && !alpha_<mode>(t1, t2)) return fail<mode>();
273
274 // The arity of a flex-rank Seq is the entire shape vector, so its rank must be pinned down first.
275 if constexpr (mode == Check) {
276 if (auto rank = isa_flex_rank(d1); rank && isa_dim(d2)) {
277 if (!check_rank(d1->as<Seq>(), rank, d2)) return fail<Check>();
278 todo = true;
279 continue;
280 }
281 if (auto rank = isa_flex_rank(d2); rank && isa_dim(d1)) {
282 if (!check_rank(d2->as<Seq>(), rank, d1)) return fail<Check>();
283 todo = true;
284 continue;
285 }
286 }
287
288 if (!alpha_<mode>(d1->arity(), d2->arity())) return fail<mode>();
289
290 auto new_d1 = d1->zonk_mut();
291 auto new_d2 = d2->zonk_mut();
292 if (new_d1 != d1 || new_d2 != d2) {
293 todo = true;
294 d1 = new_d1;
295 d2 = new_d2;
296 }
297 }
298
299 auto seq1 = d1->isa<Seq>();
300 auto seq2 = d2->isa<Seq>();
301
302 if constexpr (mode == Check) {
303 if (auto umax = d1->isa<UMax>(); umax && !d2->isa<UMax>()) return check(umax, d2);
304 if (auto umax = d2->isa<UMax>(); umax && !d1->isa<UMax>()) return check(umax, d1);
305
306 if (seq1 && seq1->arity() == world().lit_nat_1() && !seq2) return check1(seq1, d2);
307 if (seq2 && seq2->arity() == world().lit_nat_1() && !seq1) return check1(seq2, d1);
308
309 if (seq1 && seq2) {
310 if (auto mut_seq = seq1->isa_mut<Seq>(); mut_seq && seq2->isa_imm()) return check(mut_seq, seq2);
311 if (auto mut_seq = seq2->isa_mut<Seq>(); mut_seq && seq1->isa_imm()) return check(mut_seq, seq1);
312 }
313 }
314
315 if (auto prod = d1->isa<Prod>()) return check<mode>(prod, d2);
316 if (auto prod = d2->isa<Prod>()) return check<mode>(prod, d1);
317 if (seq1 && seq2) return alpha_<mode>(seq1->body(), seq2->body());
318
319 if (d1->node() != d2->node() || d1->flags() != d2->flags()) return fail<mode>();
320
321 if (auto var1 = d1->isa<Var>()) {
322 auto var2 = d2->as<Var>();
323 if (auto i = binders_.find(var1->binder()); i != binders_.end()) return i->second == var2->binder();
324 if (auto i = binders_.find(var2->binder()); i != binders_.end()) return fail<mode>(); // var2 is bound
325 // both var1 and var2 are free: OK, when they are the same or in Check mode
326 return var1 == var2 || mode == Check;
327 }
328
329 for (size_t i = 0, e = d1->num_ops(); i != e; ++i)
330 if (!alpha_<mode>(d1->op(i), d2->op(i))) return fail<mode>();
331 return true;
332}
333
334template<Checker::Mode mode>
335bool Checker::check(const Prod* prod, const Def* def) {
336 size_t a = prod->num_ops();
337 for (size_t i = 0; i != a; ++i)
338 if (!alpha_<mode>(prod->op(i), def->proj(a, i))) return fail<mode>();
339 return true;
340}
341
342// A recursive type yields `l = max(ops..., l)` for its level whose least solution is `max(ops...)`.
343static const Def* drop_self(Hole* hole, const Def* def) {
344 auto umax = def->isa<UMax>();
345 if (!umax) return def;
346
347 DefVec ops;
348 for (auto op : umax->ops())
349 if (op->zonk_mut() != hole) ops.emplace_back(op);
350
351 return ops.size() == umax->num_ops() ? def : hole->world().umax<UMax::Univ>(ops);
352}
353
354// A Hole may only be solved with a Def its type accepts; this is what pins `r` down in `s: «r; Nat»`.
355bool Checker::check(Hole* hole, const Def* def) {
356 def = drop_self(hole, def);
357
358 if (def->unfold_type()) { // Univ has no type and is assignable to nothing
359 if (auto new_def = assignable_(hole->type(), def))
360 def = new_def;
361 else
362 return fail<Check>();
363 }
364 return hole->set(def), true;
365}
366
367// alpha(«?s; body», «e₀; «e₁; … «e_{n-1}; def»…»): peel dimensions until the remainder matches body.
368// This determines the rank; Hole::tuplefy then hands the extents to the regular structural comparison.
369bool Checker::check_rank(const Seq* seq, Hole* rank, const Def* def) {
370 auto body = seq->body();
371 size_t n = 0;
372 size_t num = 0;
373
374 for (size_t i = 1; isa_dim(def); ++i) {
375 def = def->as<Seq>()->body();
376 if (alpha_<Test>(body, def)) n = i, ++num; // Test mode: probing must not solve any Hole
377 }
378
379 if (num != 1) return fail<Check>(); // no rank fits, or several do and the call site needs an explicit `@`
380
381 rank->set(world().lit_nat(n));
382 Hole::isa_unset(seq->arity()->zonk_mut())->tuplefy(n);
383 return true;
384}
385
386// alpha(«1; body», def) -> alpha(body, def);
387bool Checker::check1(const Seq* seq, const Def* def) {
388 auto body = seq->reduce(world().lit_idx_1_0()); // try to get rid of var inside of body
389 if (!alpha_<Check>(body, def)) return fail<Check>();
390 if (auto mut_seq = seq->isa_mut<Seq>()) mut_seq->set(world().lit_nat_1(), body->zonk());
391 return true;
392}
393
394// Try to get rid of mut_seq's var: it may occur in its body and vanish after reduction
395// as holes might have been filled in the meantime.
396bool Checker::check(Seq* mut_seq, const Seq* imm_seq) {
397 auto mut_body = mut_seq->reduce(world().top(world().type_idx(mut_seq->arity())));
398 if (!alpha_<Check>(mut_body, imm_seq->body())) return fail<Check>();
399
400 mut_seq->set(mut_seq->arity(), mut_body->zonk());
401 return true;
402}
403
404bool Checker::check(const UMax* umax, const Def* def) {
405 for (auto op : umax->ops())
406 if (!alpha<Check>(op, def)) return fail<Check>();
407 return true;
408}
409
410#ifndef DOXYGEN
411template bool Checker::alpha_<Checker::Check>(const Def*, const Def*);
412template bool Checker::alpha_<Checker::Test>(const Def*, const Def*);
413#endif
414
415/*
416 * infer
417 */
418
420 return w.sigma(DefVec(ops, [](const Def* op) { return op->unfold_type(); }));
421}
422
424 return w.umax<UMax::Kind>(DefVec(ops, [](const Def* op) { return op->unfold_type(); }));
425}
426
427const Def* Pi::infer(const Def* dom, const Def* codom) {
428 auto& w = dom->world();
429 return w.umax<UMax::Kind>({dom->unfold_type(), codom->unfold_type()});
430}
431
432const Def* Reform::infer(const Def* dom) { return dom->unfold_type(); }
433
434/*
435 * Def::check
436 */
437
438const Def* Def::check(size_t i, const Def* def) {
439 auto lam = isa<Lam>();
440 if (!lam) return def; // TODO Pi/Sigma/Arr/Rule accept any op for now
441
442 if (i == 0) {
443 if (auto filter = Checker::assignable(world().type_bool(), def)) return filter;
444 def->blame("filter `{}` of lambda is of type `{}` but must be of type `Bool`", def, type_of(def)).bail();
445 }
446 assert(i == 1);
447 if (auto body = Checker::assignable(lam->codom(), def)) return body;
448 def->blame("function body is not assignable to its declared codomain")
449 .n("expected `{}`, got `{}`", lam->codom(), type_of(def))
450 .n(lam->codom()->loc(), "codomain `{}` declared here", lam->codom())
451 .bail();
452}
453
454const Def* Def::check() {
455 auto& w = world();
456
457 switch (node()) {
458 case Node::Pi: {
459 auto pi = as<Pi>();
460 auto t = Pi::infer(pi->dom(), pi->codom());
462 type()
463 ->blame("declared sort `{}` of function type does not match inferred sort `{}`", type(), t)
464 .bail();
465 return t;
466 }
467 case Node::Arr: {
468 auto t = as<Arr>()->body()->unfold_type();
470 type()->blame("declared sort `{}` of array does not match inferred sort `{}`", type(), t).bail();
471 return t;
472 }
473 case Node::Reform: {
474 auto t = Reform::infer(as<Reform>()->dom());
476 type()->blame("declared sort `{}` of rule type does not match inferred sort `{}`", type(), t).bail();
477 return t;
478 }
479 case Node::Sigma: {
480 auto t = Sigma::infer(w, ops());
481 if (t == type() || Checker::alpha<Checker::Check>(t, type())) return t; // TODO HACK
482 w.log().w("expected type {} for {} but keeping the declared {} due to clos-conv bugs", t, this, type());
483 return type();
484 }
485 case Node::Rule: {
486 auto rule = as<Rule>();
487 auto t1 = rule->lhs()->unfold_type();
488 auto t2 = rule->rhs()->unfold_type();
490 type()
491 ->blame("type mismatch between rule sides: lhs has type `{}` but rhs has type `{}`", t1, t2)
492 .bail();
493 if (!Checker::assignable(w.type_bool(), rule->guard()))
494 rule->guard()
495 ->blame("condition `{}` of rewrite rule is of type `{}` but must be of type `Bool`", rule->guard(),
496 type_of(rule->guard()))
497 .bail();
498 return type();
499 }
500 default: return type();
501 }
502}
503
504} // namespace mim
A (possibly paramterized) Array.
Definition tuple.h:110
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:121
static bool alpha(const Def *d1, const Def *d2)
Definition check.h:93
World & world()
Definition check.h:81
@ Check
In Mode::Check, type inference is happening and Holes will be resolved, if possible.
Definition check.h:86
static const Def * assignable(const Def *type, const Def *value)
Can value be assigned to sth of type?
Definition check.h:100
Base class for all Defs.
Definition def.h:273
bool is_set() const
Definition def.h:370
const Def * zonk_mut() const
If mutable, zonk()s all ops and tries to immutabilize it; otherwise just zonk.
Definition check.cpp:27
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:623
constexpr Node node() const noexcept
Definition def.h:297
bool has_dep() const noexcept
Definition def.h:408
Defs deps() const noexcept
Definition def.cpp:468
const Def * zonk() const
If Holes have been filled, reconstruct the program without them.
Definition check.cpp:21
World & world() const noexcept
Definition def.h:1097
constexpr auto ops() const noexcept
Definition def.h:348
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:580
const Def * op(size_t i) const noexcept
Definition def.h:351
std::pair< D *, const Var * > isa_binder() const
Is this a mutable that introduces a Var?
Definition def.h:490
const Def * var(nat_t a, nat_t i) noexcept
Definition def.h:479
const Def * unfold_type() const
Yields the type of this Def and builds a new Type (UInc n) if necessary.
Definition def.cpp:451
Muts local_muts() const
Mutables reachable by following immutable deps(); mut->local_muts() is by definition the set { mut }...
Definition def.h:507
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1111
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:310
const Def * arity() const
Number of elements available to Extract / Insert (may be dynamic).
Definition def.cpp:592
constexpr auto reduce(const Def *arg) const
Definition def.h:660
const T * isa_imm() const
Definition def.h:574
bool needs_zonk() const
Yields true, if Def::local_muts() contain a Hole that is set.
Definition check.cpp:12
const Def * check()
After all Def::ops have been Def::set, this method will be invoked to check the type of this mutable.
Definition check.cpp:454
This node is a hole in the IR that is inferred by its context later on.
Definition check.h:16
std::pair< Hole *, const Def * > find()
Transitively walks up Holes until the last one while path-compressing everything.
Definition check.cpp:59
Hole * set(const Def *op)
Definition check.h:35
const Def * tuplefy(nat_t)
If unset, explode to Tuple.
Definition check.cpp:82
static Hole * isa_unset(const Def *def)
Definition check.h:53
static const Def * isa_set(const Def *def)
Definition check.h:48
A dependent function type.
Definition lam.h:14
static const Def * infer(const Def *dom, const Def *codom)
Definition check.cpp:427
bool is_implicit() const
Definition lam.h:26
const Def * dom() const
Definition lam.h:35
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
Base class for Sigma and Tuple.
Definition tuple.h:10
static constexpr bool isa_node(mim::Node n) noexcept
Prod groups Sigma and Tuple; see fe::NodeSetable.
Definition tuple.h:16
Def(World *, Node, const Def *type, Defs ops, flags_t flags)
Constructor for an immutable Def.
Definition def.cpp:42
static const Def * infer(const Def *dom)
Definition check.cpp:432
const Def * dom() const
Definition rule.h:18
Base class for Arr and Pack.
Definition tuple.h:75
static constexpr bool isa_node(mim::Node n) noexcept
Seq groups Arr and Pack; see fe::NodeSetable.
Definition tuple.h:81
friend class World
Definition tuple.h:56
static const Def * infer(World &, Defs)
Definition check.cpp:423
friend class World
Definition tuple.h:71
static const Def * infer(World &, Defs)
Definition check.cpp:419
@ Univ
Definition def.h:864
@ Kind
Definition def.h:864
VarRewriter(World &world)
Definition rewrite.h:118
Zonker & zonker()
Definition world.h:107
const Def * umax(Defs)
Definition world.cpp:171
auto & vars()
Definition world.h:694
const Def * rewire_mut(Def *)
Definition rewrite.cpp:352
const Def * rewrite(const Def *) final
Definition rewrite.cpp:343
Definition ast.h:16
u64 nat_t
Definition types.h:37
static Hole * isa_flex_rank(const Def *def)
The rank of «s; T» with s: «r; Nat» is unknown as long as r is: World::seq cannot un-nest it yet.
Definition check.cpp:195
static const Def * drop_self(Hole *hole, const Def *def)
Definition check.cpp:343
@ Hole
Depends on a Hole.
Definition def.h:137
fe::View< const Def * > Defs
Definition def.h:91
static bool isa_dim(const Def *def)
Is def a Seq that spans exactly one dimension, i.e. one that a rank can be peeled off?
Definition check.cpp:188
TExt< true > Top
Definition lattice.h:165
fe::Vector< const Def * > DefVec
Definition def.h:93
auto type_of(const Def *def)
Def::unfold_type of def for a diagnostic - Univ is the one Def that has no type at all.
Definition def.h:1136
static bool is_flex(const Def *def)
Definition check.cpp:205
@ Pi
Definition def.h:122
@ Arr
Definition def.h:122
@ Var
Definition def.h:122
@ Hole
Definition def.h:122
@ Reform
Definition def.h:122
@ Sigma
Definition def.h:122
@ Top
Definition def.h:122
@ Rule
Definition def.h:122
@ UMax
Definition def.h:122