Spicy
type.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <unordered_set>
6 #include <utility>
7 
8 #include <hilti/ast/id.h>
9 #include <hilti/ast/node.h>
10 #include <hilti/base/type_erase.h>
11 #include <hilti/base/util.h>
12 
13 namespace hilti {
14 
15 namespace trait {
17 class isType : public isNode {};
18 } // namespace trait
19 
20 class Type;
21 
22 namespace declaration {
23 class Parameter;
24 }
25 
26 namespace type {
27 
28 namespace function {
29 using Parameter = declaration::Parameter;
30 }
31 
32 namespace trait {
33 class isAllocable {};
34 class isSortable {};
36 class isIterable {};
37 class isIterator {};
38 class isMutable {};
39 class isParameterized {};
40 class isReferenceType {};
42 class isView {};
43 class isViewable {};
45 class takesArguments {};
46 } // namespace trait
47 
48 using ResolvedState = std::unordered_set<uintptr_t>;
49 
51 enum class Flag {
53  Constant = (1U << 0U),
54 
56  NonConstant = (1U << 1U),
57 
62  NoInheritScope = (1U << 2U),
63 
66  PruneWalk = (1U << 3U),
67 };
68 
74 class Flags {
75 public:
76  Flags() = default;
77  Flags(Flag f) : _flags(static_cast<uint64_t>(f)) {}
78  Flags(const Flags&) = default;
79  Flags(Flags&&) noexcept = default;
80  ~Flags() = default;
81 
83  bool has(Flag f) const { return _flags & static_cast<uint64_t>(f); }
84 
86  void set(type::Flag flag, bool set = true) {
87  if ( set )
88  _flags |= static_cast<uint64_t>(flag);
89  else
90  _flags &= ~static_cast<uint64_t>(flag);
91  }
92 
93  Flags operator+(Flag f) {
94  auto x = Flags(*this);
95  x.set(f);
96  return x;
97  }
98 
99  Flags operator+(const Flags& other) const {
100  auto x = Flags();
101  x._flags = _flags | other._flags;
102  return x;
103  }
104 
105  Flags& operator+=(Flag f) {
106  set(f);
107  return *this;
108  }
109  Flags& operator+=(const Flags& other) {
110  _flags |= other._flags;
111  return *this;
112  }
113 
114  Flags operator-(const Flags& other) const {
115  auto x = Flags();
116  x._flags = _flags & ~other._flags;
117  return x;
118  }
119 
120  Flags& operator-=(Flag f) {
121  set(f, false);
122  return *this;
123  }
124  Flags& operator-=(const Flags& other) {
125  _flags &= ~other._flags;
126  return *this;
127  }
128 
129  Flags& operator=(Flag f) {
130  set(f);
131  return *this;
132  }
133  Flags& operator=(const Flags&) = default;
134  Flags& operator=(Flags&&) noexcept = default;
135 
136  bool operator==(Flags other) const { return _flags == other._flags; }
137 
138  bool operator!=(Flags other) const { return _flags != other._flags; }
139 
140 private:
141  uint64_t _flags = 0;
142 };
143 
144 inline Flags operator+(Flag f1, Flag f2) { return Flags(f1) + f2; }
145 
146 namespace detail {
147 
148 struct State {
149  std::optional<ID> id;
150  std::optional<ID> cxx;
151  std::optional<ID> resolved_id;
153 };
154 
155 #include <hilti/autogen/__type.h>
156 } // namespace detail
157 
158 } // namespace type
159 
160 class Type : public type::detail::Type {
161 public:
162  using type::detail::Type::Type;
163 
164  std::optional<ID> resolvedID() const { return _state().resolved_id; }
165 
166  void setCxxID(ID id) {
167  // We always normalize cxx IDs so they refer to fully qualified names.
168  if ( ! util::startsWith(id, "::") )
169  _state().cxx = util::fmt("::%s", id);
170  else
171  _state().cxx = std::move(id);
172  }
173 
174  void setTypeID(ID id) { _state().id = std::move(id); }
175  void addFlag(type::Flag f) { _state().flags += f; }
176 
178  bool hasFlag(type::Flag f) const { return _state().flags.has(f); }
180  const type::Flags& flags() const { return _state().flags; }
182  bool _isConstant() const { return _state().flags.has(type::Flag::Constant); }
184  const std::optional<ID>& typeID() const { return _state().id; }
186  const std::optional<ID>& cxxID() const { return _state().cxx; }
188  const type::detail::State& _state() const { return _state_; }
190  type::detail::State& _state() { return _state_; }
192  bool pruneWalk() const { return hasFlag(type::Flag::PruneWalk); }
193 };
194 
196 inline Node to_node(Type t) { return Node(std::move(t)); }
197 
199 inline std::ostream& operator<<(std::ostream& out, Type t) { return out << to_node(std::move(t)); }
200 
206 class TypeBase : public NodeBase, public hilti::trait::isType {
207 public:
208  using NodeBase::NodeBase;
209 };
210 
211 namespace type {
212 namespace detail {
213 extern void applyPruneWalk(hilti::Type& t);
214 } // namespace detail
215 
216 inline Type pruneWalk(Type t) {
217  detail::applyPruneWalk(t);
218  return t;
219 }
220 
228 inline hilti::Type addFlags(const Type& t, const type::Flags& flags) {
229  auto x = Type(t);
230  x._state().flags += flags;
231  return x;
232 }
233 
241 inline hilti::Type removeFlags(const Type& t, const type::Flags& flags) {
242  auto x = Type(t);
243  x._state().flags -= flags;
244  return x;
245 }
246 
254 inline hilti::Type setCxxID(const Type& t, ID id) {
255  auto x = Type(t);
256  x._state().cxx = std::move(id);
257  return x;
258 }
259 
267 inline hilti::Type setTypeID(const Type& t, ID id) {
268  auto x = Type(t);
269  x._state().id = std::move(id);
270  return x;
271 }
272 
277 class Wildcard {};
278 
280 inline bool isAllocable(const Type& t) { return t._isAllocable(); }
281 
283 inline bool isSortable(const Type& t) { return t._isSortable(); }
284 
286 inline bool isDereferenceable(const Type& t) { return t._isDereferenceable(); }
287 
289 inline bool isIterable(const Type& t) { return t._isIterable(); }
290 
292 inline bool isIterator(const Type& t) { return t._isIterator(); }
293 
295 inline bool isParameterized(const Type& t) { return t._isParameterized(); }
296 
298 inline bool isReferenceType(const Type& t) { return t._isReferenceType(); }
299 
301 inline bool isMutable(const Type& t) { return t._isMutable(); }
302 
304 inline bool isRuntimeNonTrivial(const Type& t) { return t._isRuntimeNonTrivial(); }
305 
307 inline bool isView(const Type& t) { return t._isView(); }
308 
310 inline bool isViewable(const Type& t) { return t._isViewable(); }
311 
313 inline bool takesArguments(const Type& t) { return t._takesArguments(); }
314 
322 inline bool isConstant(const Type& t) {
323  return t.flags().has(type::Flag::Constant) || (! isMutable(t) && ! t.flags().has(type::Flag::NonConstant));
324 }
325 
327 inline auto constant(Type t) {
328  t._state().flags -= type::Flag::NonConstant;
329  t._state().flags += type::Flag::Constant;
330  return t;
331 }
332 
337 inline auto nonConstant(Type t, bool force = false) {
338  t._state().flags -= type::Flag::Constant;
339 
340  if ( force )
341  t._state().flags += type::Flag::NonConstant;
342 
343  return t;
344 }
345 
346 namespace detail {
347 // Internal backends for the `isResolved()`.
348 extern bool isResolved(const hilti::Type& t, ResolvedState* rstate);
349 
350 inline bool isResolved(const std::optional<hilti::Type>& t, ResolvedState* rstate) {
351  return t.has_value() ? isResolved(*t, rstate) : true;
352 }
353 
354 inline bool isResolved(const std::optional<const hilti::Type>& t, ResolvedState* rstate) {
355  return t.has_value() ? isResolved(*t, rstate) : true;
356 }
357 } // namespace detail
358 
360 extern bool isResolved(const Type& t);
361 
363 inline bool isResolved(const std::optional<Type>& t) { return t.has_value() ? isResolved(*t) : true; }
364 
366 inline bool isResolved(const std::optional<const Type>& t) { return t.has_value() ? isResolved(*t) : true; }
367 
369 inline bool sameExceptForConstness(const Type& t1, const Type& t2) {
370  if ( &t1 == &t2 )
371  return true;
372 
373  if ( t1.typeID() && t2.typeID() )
374  return *t1.typeID() == *t2.typeID();
375 
376  if ( t1.cxxID() && t2.cxxID() )
377  return *t1.cxxID() == *t2.cxxID();
378 
379  return t1.isEqual(t2) || t2.isEqual(t1);
380 }
381 
382 } // namespace type
383 
384 inline bool operator==(const Type& t1, const Type& t2) {
385  if ( &t1 == &t2 )
386  return true;
387 
388  if ( type::isMutable(t1) || type::isMutable(t2) ) {
389  if ( type::isConstant(t1) && ! type::isConstant(t2) )
390  return false;
391 
392  if ( type::isConstant(t2) && ! type::isConstant(t1) )
393  return false;
394  }
395 
396  if ( t1.typeID() && t2.typeID() )
397  return *t1.typeID() == *t2.typeID();
398 
399  if ( t1.cxxID() && t2.cxxID() )
400  return *t1.cxxID() == *t2.cxxID();
401 
402  // Type comparison is not fully symmetric, it's good enough
403  // if one type believes it matches the other one.
404  return t1.isEqual(t2) || t2.isEqual(t1);
405 }
406 
407 inline bool operator!=(const Type& t1, const Type& t2) { return ! (t1 == t2); }
408 
410 template<typename T, typename std::enable_if_t<std::is_base_of<trait::isType, T>::value>* = nullptr>
411 inline Node to_node(T t) {
412  return Node(Type(std::move(t)));
413 }
414 
415 } // namespace hilti
Definition: type.h:148
const std::optional< ID > & typeID() const
Definition: type.h:184
Definition: type.h:34
Definition: type.h:17
Definition: type.h:37
type::detail::State & _state()
Definition: type.h:190
Definition: type.h:38
Definition: type.h:160
const std::optional< ID > & cxxID() const
Definition: type.h:186
Definition: type.h:42
Definition: type.h:36
Definition: parameter.h:46
const type::detail::State & _state() const
Definition: type.h:188
Definition: type.h:74
bool has(Flag f) const
Definition: type.h:83
Definition: type.h:206
Definition: type.h:43
Definition: node.h:111
Definition: type.h:33
Definition: type.h:277
Definition: node.h:21
bool pruneWalk() const
Definition: type.h:192
bool hasFlag(type::Flag f) const
Definition: type.h:178
Definition: elements.cc:17
const type::Flags & flags() const
Definition: type.h:180
Definition: id.h:18
bool _isConstant() const
Definition: type.h:182
Definition: node.h:359