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 /// If unset, explode to Tuple.
43 /// @returns the new Tuple, or `this` if unsuccessful.
44 const Def* tuplefy(nat_t);
45
46 /// @name isa
47 ///@{
48 static const Def* isa_set(const Def* def) {
49 if (auto hole = def->isa<Hole>(); hole && hole->is_set()) return hole->op();
50 return nullptr;
51 }
52
53 static Hole* isa_unset(const Def* def) {
54 if (auto hole = def->isa_mut<Hole>(); hole && !hole->is_set()) return hole;
55 return nullptr;
56 }
57
58 /// If @p def is a Hole, find last in chain, otherwise yields @p def again.
59 static const Def* find(const Def* def) {
60 if (auto hole = def->isa_mut<Hole>()) {
61 auto [last, op] = hole->find();
62 return op ? op : last;
63 }
64 return def;
65 }
66 ///@}
67
68 static constexpr auto Node = mim::Node::Hole;
69 static constexpr size_t Num_Ops = 1;
70
71private:
72 friend class World;
73 friend class Checker;
74};
75
76class Checker {
77public:
78 explicit Checker(World& world)
79 : world_(world) {}
80
81 World& world() { return world_; }
82
83 enum Mode {
84 /// In Mode::Check, type inference is happening and Hole%s will be resolved, if possible.
85 /// Also, two *free* but *different* Var%s **are** considered α-equivalent.
87 /// In Mode::Test, no type inference is happening and Hole%s will not be touched.
88 /// Also, Two *free* but *different* Var%s are **not** considered α-equivalent.
90 };
91
92 template<Mode mode>
93 static bool alpha(const Def* d1, const Def* d2) {
94 if (d1 == d2) return true;
95 return Checker(d1->world()).alpha_<mode>(d1, d2);
96 }
97
98 /// Can @p value be assigned to sth of @p type?
99 /// @note This is different from `equiv(type, value->type())` since @p type may be dependent.
100 [[nodiscard]] static const Def* assignable(const Def* type, const Def* value) {
101 if (type == value->type()) return value;
102 return Checker(type->world()).assignable_(type, value);
103 }
104
105 /// Yields `defs.front()`, if all @p defs are Check::alpha-equivalent (`Mode::Test`) and `nullptr` otherwise.
106 static const Def* is_uniform(Defs defs);
107
108private:
109#ifdef MIM_ENABLE_CHECKS
110 template<Mode>
111 bool fail();
112 const Def* fail();
113#else
114 template<Mode>
115 bool fail() {
116 return false;
117 }
118 const Def* fail() { return {}; }
119#endif
120
121 [[nodiscard]] const Def* assignable_(const Def* type, const Def* value);
122 template<Mode>
123 [[nodiscard]] bool alpha_(const Def* d1, const Def* d2);
124 template<Mode>
125 [[nodiscard]] bool alpha_impl_(const Def* d1, const Def* d2);
126
127 /// Fast path for alpha_ that needs neither binders_ nor memo_ and hence doesn't allocate.
128 /// @returns `std::nullopt`, if the full alpha_impl_ machinery is needed.
129 template<Mode>
130 [[nodiscard]] std::optional<bool> try_alpha_(const Def* d1, const Def* d2);
131
132 template<Mode>
133 [[nodiscard]] bool check(const Prod*, const Def*);
134 template<Mode>
135 [[nodiscard]] bool check(const Seq*, const Def*);
136 [[nodiscard]] bool check(Hole*, const Def*);
137 [[nodiscard]] bool check_rank(const Seq*, Hole* rank, const Def*);
138 [[nodiscard]] bool check1(const Seq*, const Def*);
139 [[nodiscard]] bool check(Seq*, const Seq*);
140 [[nodiscard]] bool check(const UMax*, const Def*);
141
142 /// Symmetric key for the alpha_ memo: the two gids packed into one u64, smaller first.
143 /// Canonicalizing by gid is what lets alpha_ get away with a single probe.
144 static constexpr u64 memo_key(const Def* d1, const Def* d2) noexcept {
145 auto g1 = u64(d1->gid()), g2 = u64(d2->gid());
146 return g1 < g2 ? g1 << 32 | g2 : g2 << 32 | g1;
147 }
148
149 using Binders = MutMap<const Def*>;
150 std::pair<Binders::iterator, bool> bind(Def* mut, const Def* d);
151
152 World& world_;
153 Binders binders_;
154 Vars bound_; ///< Var%s of all binders_; these are the Var%s subject to renaming.
155 std::array<absl::flat_hash_set<u64>, 2> memo_;
156};
157
158} // 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:121
static bool alpha(const Def *d1, const Def *d2)
Definition check.h:93
Checker(World &world)
Definition check.h:78
World & world()
Definition check.h:81
@ Test
In Mode::Test, no type inference is happening and Holes will not be touched.
Definition check.h:89
@ 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
Def * set(size_t i, const Def *)
Successively set from left to right.
Definition def.cpp:196
World & world() const noexcept
Definition def.h:1097
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
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
Definition def.h:1111
Def * unset()
Unsets all Def::ops; works even, if not set at all or only partially set.
Definition def.cpp:213
static constexpr size_t Num_Ops
Definition check.h:69
std::pair< Hole *, const Def * > find()
Transitively walks up Holes until the last one while path-compressing everything.
Definition check.cpp:59
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:59
friend class Checker
Definition check.h:73
const Def * tuplefy(nat_t)
If unset, explode to Tuple.
Definition check.cpp:82
static constexpr auto Node
Definition check.h:68
friend class World
Definition check.h:72
static Hole * isa_unset(const Def *def)
Definition check.h:53
Hole * unset()
Definition check.h:39
static const Def * isa_set(const Def *def)
Definition check.h:48
Base class for Sigma and Tuple.
Definition tuple.h:10
Base class for Arr and Pack.
Definition tuple.h:75
CRTP-based mixin to declare setters for Def::loc & Def::name using a covariant return type.
Definition def.h:209
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:40
Definition ast.h:16
u64 nat_t
Definition types.h:37
fe::View< const Def * > Defs
Definition def.h:91
fe::Patricia< const Var, DefKey >::Set Vars
Definition def.h:112
uint64_t u64
Definition types.h:27
@ Hole
Definition def.h:122