MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
check.h
Go to the documentation of this file.
1#pragma once
2
3#include <absl/container/flat_hash_set.h>
4
5#include "mim/def.h"
6
7namespace mim {
8
9class Prod;
10class Seq;
11
12/// This node is a hole in the IR that is inferred by its context later on.
13/// It is modelled as a *mutable* Def.
14/// If inference was successful, it's Hole::op will be set to the inferred Def.
15/// @note Hole%s are not type-checked as they are used during type-checking - causing a chicken-and-egg problem.
16class Hole : public Def, public Setters<Hole> {
17private:
18 Hole(const Def* type)
19 : Def(Node, type, 1, 0) {}
20
21public:
22 using Setters<Hole>::set;
23
24 /// @name op
25 ///@{
26 const Def* op() const { return Def::op(0); }
27
28 /// Transitively walks up Hole%s until the last one while path-compressing everything.
29 /// @returns the final Hole in the chain and final op() (if any).
30 std::pair<Hole*, const Def*> find();
31 ///@}
32
33 /// @name set/unset
34 ///@{
35 Hole* set(const Def* op) {
36 assert(op != this);
37 return Def::set({op})->as<Hole>();
38 }
39 Hole* unset() { return Def::unset()->as<Hole>(); }
40 ///@}
41
42 Hole* stub(const Def* type) { return stub_(world(), type)->set(dbg()); }
43
44 /// If unset, explode to Tuple.
45 /// @returns the new Tuple, or `this` if unsuccessful.
46 const Def* tuplefy(nat_t);
47
48 /// @name isa
49 ///@{
50 static const Def* isa_set(const Def* def) {
51 if (auto hole = def->isa<Hole>(); hole && hole->is_set()) return hole->op();
52 return nullptr;
53 }
54
55 static Hole* isa_unset(const Def* def) {
56 if (auto hole = def->isa_mut<Hole>(); hole && !hole->is_set()) return hole;
57 return nullptr;
58 }
59
60 /// If @p def is a Hole, find last in chain, otherwise yields @p def again.
61 static const Def* find(const Def* def) {
62 if (auto hole = def->isa_mut<Hole>()) {
63 auto [last, op] = hole->find();
64 return op ? op : last;
65 }
66 return def;
67 }
68 ///@}
69
70 static constexpr auto Node = mim::Node::Hole;
71 static constexpr size_t Num_Ops = 1;
72
73private:
74 const Def* rebuild_(World&, const Def*, Defs) const final;
75 Hole* stub_(World&, const Def*) final;
76
77 friend class World;
78 friend class Checker;
79};
80
81class Checker {
82public:
83 explicit Checker(World& world)
84 : world_(world) {}
85
86 World& world() { return world_; }
87
88 enum Mode {
89 /// In Mode::Check, type inference is happening and Hole%s will be resolved, if possible.
90 /// Also, two *free* but *different* Var%s **are** considered α-equivalent.
92 /// In Mode::Test, no type inference is happening and Hole%s will not be touched.
93 /// Also, Two *free* but *different* Var%s are **not** considered α-equivalent.
95 };
96
97 template<Mode mode>
98 static bool alpha(const Def* d1, const Def* d2) {
99 if (d1 == d2) return true;
100 return Checker(d1->world()).alpha_<mode>(d1, d2);
101 }
102
103 /// Can @p value be assigned to sth of @p type?
104 /// @note This is different from `equiv(type, value->type())` since @p type may be dependent.
105 [[nodiscard]] static const Def* assignable(const Def* type, const Def* value) {
106 if (type == value->type()) return value;
107 return Checker(type->world()).assignable_(type, value);
108 }
109
110 /// Yields `defs.front()`, if all @p defs are Check::alpha-equivalent (`Mode::Test`) and `nullptr` otherwise.
111 static const Def* is_uniform(Defs defs);
112
113private:
114#ifdef MIM_ENABLE_CHECKS
115 template<Mode>
116 bool fail();
117 const Def* fail();
118#else
119 template<Mode>
120 bool fail() {
121 return false;
122 }
123 const Def* fail() { return {}; }
124#endif
125
126 [[nodiscard]] const Def* assignable_(const Def* type, const Def* value);
127 template<Mode>
128 [[nodiscard]] bool alpha_(const Def* d1, const Def* d2);
129 template<Mode>
130 [[nodiscard]] bool alpha_impl_(const Def* d1, const Def* d2);
131 template<Mode>
132 [[nodiscard]] bool check(const Prod*, const Def*);
133 template<Mode>
134 [[nodiscard]] bool check(const Seq*, const Def*);
135 [[nodiscard]] bool check1(const Seq*, const Def*);
136 [[nodiscard]] bool check(Seq*, const Seq*);
137 [[nodiscard]] bool check(const UMax*, const Def*);
138
139 auto bind(Def* mut, const Def* d) {
140 if (!mut) return std::pair(binders_.end(), true);
141 auto res = binders_.emplace(mut, d);
142 // A new binding may change how bound Var%s compare, so positive memo entries may become invalid.
143 if (res.second) {
144 memo_[Check].clear();
145 memo_[Test].clear();
146 }
147 return res;
148 }
149
150 World& world_;
151 MutMap<const Def*> binders_;
152 std::array<absl::flat_hash_set<std::pair<const Def*, const Def*>>, 2> memo_;
153};
154
155} // namespace mim
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:114
static bool alpha(const Def *d1, const Def *d2)
Definition check.h:98
Checker(World &world)
Definition check.h:83
World & world()
Definition check.h:86
@ Test
In Mode::Test, no type inference is happening and Holes will not be touched.
Definition check.h:94
@ Check
In Mode::Check, type inference is happening and Holes will be resolved, if possible.
Definition check.h:91
static const Def * assignable(const Def *type, const Def *value)
Can value be assigned to sth of type?
Definition check.h:105
Base class for all Defs.
Definition def.h:261
bool is_set() const
Yields true if empty or the last op is set.
Definition def.cpp:308
Def * set(size_t i, const Def *)
Successively set from left to right.
Definition def.cpp:276
World & world() const noexcept
Definition def.cpp:483
T * isa_mut() const
If this is mutable, it will cast constness away and perform a dynamic_cast to T.
Definition def.h:527
const Def * op(size_t i) const noexcept
Definition def.h:320
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.cpp:491
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
Definition def.cpp:299
Dbg dbg() const
Definition def.h:556
static constexpr size_t Num_Ops
Definition check.h:71
std::pair< Hole *, const Def * > find()
Transitively walks up Holes until the last one while path-compressing everything.
Definition check.cpp:50
const Def * op() const
Definition check.h:26
Hole * set(const Def *op)
Definition check.h:35
static const Def * find(const Def *def)
If def is a Hole, find last in chain, otherwise yields def again.
Definition check.h:61
friend class Checker
Definition check.h:78
const Def * tuplefy(nat_t)
If unset, explode to Tuple.
Definition check.cpp:75
static constexpr auto Node
Definition check.h:70
const Def * rebuild_(World &, const Def *, Defs) const final
Definition def.cpp:119
friend class World
Definition check.h:77
static Hole * isa_unset(const Def *def)
Definition check.h:55
Hole * stub(const Def *type)
Definition check.h:42
Hole * unset()
Definition check.h:39
Hole * stub_(World &, const Def *) final
Definition def.cpp:162
static const Def * isa_set(const Def *def)
Definition check.h:50
Base class for Sigma and Tuple.
Definition tuple.h:10
Base class for Arr and Pack.
Definition tuple.h:86
CRTP-based mixin to declare setters for Def::loc & Def::name using a covariant return type.
Definition def.h:193
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:36
Definition ast.h:14
View< const Def * > Defs
Definition def.h:78
u64 nat_t
Definition types.h:37
@ Hole
Definition def.h:109
@ UMax
Definition def.h:109
GIDMap< Def *, To > MutMap
Definition def.h:86