P4C
The P4 Compiler
Loading...
Searching...
No Matches
interpreter.h
1/*
2Copyright 2016 VMware, Inc.
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14*/
15
16#ifndef MIDEND_INTERPRETER_H_
17#define MIDEND_INTERPRETER_H_
18
19#include "frontends/common/resolveReferences/referenceMap.h"
20#include "frontends/p4/coreLibrary.h"
21#include "frontends/p4/typeMap.h"
22#include "ir/ir.h"
23
24// Symbolic P4 program evaluation.
25
26namespace P4 {
27
28class SymbolicValueFactory;
29
30// Base class for all abstract values
31class SymbolicValue : public IHasDbPrint, public ICastable {
32 static unsigned crtid;
33
34 protected:
35 explicit SymbolicValue(const IR::Type *type) : id(crtid++), type(type) {}
36
37 public:
38 const unsigned id;
39 const IR::Type *type;
40 virtual bool isScalar() const = 0;
41 virtual SymbolicValue *clone() const = 0;
42 virtual void setAllUnknown() = 0;
43 virtual void assign(const SymbolicValue *other) = 0;
44 // Merging two symbolic values; values should form a lattice.
45 // Returns 'true' if merging changed the current value.
46 virtual bool merge(const SymbolicValue *other) = 0;
47 virtual bool equals(const SymbolicValue *other) const = 0;
48 // True if some parts of this value are definitely uninitialized
49 virtual bool hasUninitializedParts() const = 0;
50
51 DECLARE_TYPEINFO(SymbolicValue);
52};
53
54// Creates values from type declarations
56 const TypeMap *typeMap;
57
58 public:
59 explicit SymbolicValueFactory(const TypeMap *typeMap) : typeMap(typeMap) {
60 CHECK_NULL(typeMap);
61 }
62 SymbolicValue *create(const IR::Type *type, bool uninitialized) const;
63 // True if type has a fixed width, i.e., it does not contain a Varbit.
64 bool isFixedWidth(const IR::Type *type) const;
65 // If type has a fixed width return width in bits.
66 // varbit types are assumed to have width 0 when counting.
67 // Does not count the size for the "valid" bit for headers.
68 unsigned getWidth(const IR::Type *type) const;
69};
70
71class ValueMap final : public IHasDbPrint {
72 public:
73 std::map<const IR::IDeclaration *, SymbolicValue *> map;
74 ValueMap *clone() const {
75 auto result = new ValueMap();
76 for (auto v : map) result->map.emplace(v.first, v.second->clone());
77 return result;
78 }
79 ValueMap *filter(std::function<bool(const IR::IDeclaration *, const SymbolicValue *)> filter) {
80 auto result = new ValueMap();
81 for (auto v : map)
82 if (filter(v.first, v.second)) result->map.emplace(v.first, v.second);
83 return result;
84 }
85 void set(const IR::IDeclaration *left, SymbolicValue *right) {
86 CHECK_NULL(left);
87 CHECK_NULL(right);
88 map[left] = right;
89 }
90 SymbolicValue *get(const IR::IDeclaration *left) const {
91 CHECK_NULL(left);
92 return ::P4::get(map, left);
93 }
94
95 void dbprint(std::ostream &out) const {
96 bool first = true;
97 for (auto f : map) {
98 if (!first) out << std::endl;
99 out << f.first << "=>" << f.second;
100 first = false;
101 }
102 }
103 bool merge(const ValueMap *other) {
104 bool change = false;
105 BUG_CHECK(map.size() == other->map.size(), "Merging incompatible maps?");
106 for (auto d : map) {
107 auto v = other->get(d.first);
108 CHECK_NULL(v);
109 change = change || d.second->merge(v);
110 }
111 return change;
112 }
113 bool equals(const ValueMap *other) const {
114 BUG_CHECK(map.size() == other->map.size(), "Incompatible maps compared");
115 for (auto v : map) {
116 auto ov = other->get(v.first);
117 CHECK_NULL(ov);
118 if (!v.second->equals(ov)) return false;
119 }
120 return true;
121 }
122};
123
125 ReferenceMap *refMap;
126 TypeMap *typeMap; // updated if constant folding happens
127 ValueMap *valueMap;
128 const SymbolicValueFactory *factory;
129 bool evaluatingLeftValue = false;
130
131 std::map<const IR::Expression *, SymbolicValue *> value;
132
133 SymbolicValue *set(const IR::Expression *expression, SymbolicValue *v) {
134 LOG2("Symbolic evaluation of " << expression << " is " << v);
135 value.emplace(expression, v);
136 return v;
137 }
138
139 void postorder(const IR::Constant *expression) override;
140 void postorder(const IR::BoolLiteral *expression) override;
141 void postorder(const IR::Operation_Ternary *expression) override;
142 void postorder(const IR::Operation_Binary *expression) override;
143 void postorder(const IR::Operation_Relation *expression) override;
144 void postorder(const IR::Operation_Unary *expression) override;
145 void postorder(const IR::PathExpression *expression) override;
146 void postorder(const IR::Member *expression) override;
147 bool preorder(const IR::ArrayIndex *expression) override;
148 void postorder(const IR::ArrayIndex *expression) override;
149 void postorder(const IR::ListExpression *expression) override;
150 void postorder(const IR::StructExpression *expression) override;
151 void postorder(const IR::MethodCallExpression *expression) override;
152 void checkResult(const IR::Expression *expression, const IR::Expression *result);
153 void setNonConstant(const IR::Expression *expression);
154
155 public:
156 ExpressionEvaluator(ReferenceMap *refMap, TypeMap *typeMap, ValueMap *valueMap)
157 : refMap(refMap), typeMap(typeMap), valueMap(valueMap) {
158 CHECK_NULL(refMap);
159 CHECK_NULL(typeMap);
160 CHECK_NULL(valueMap);
161 factory = new SymbolicValueFactory(typeMap);
162 }
163
164 // May mutate the valueMap, when evaluating expression with side-effects.
165 // If leftValue is true we are returning a leftValue.
166 SymbolicValue *evaluate(const IR::Expression *expression, bool leftValue);
167
168 SymbolicValue *get(const IR::Expression *expression) const {
169 auto r = ::P4::get(value, expression);
170 BUG_CHECK(r != nullptr, "no evaluation for %1%", expression);
171 return r;
172 }
173};
174
176
177// produced when evaluation gives a static error
179 public:
180 const IR::Node *errorPosition;
181 explicit SymbolicError(const IR::Node *errorPosition)
182 : SymbolicValue(nullptr), errorPosition(errorPosition) {}
183 void setAllUnknown() override {}
184 void assign(const SymbolicValue *) override {}
185 bool isScalar() const override { return true; }
186 bool merge(const SymbolicValue *) override {
187 BUG("%1%: cannot merge errors", this);
188 return false;
189 }
190 virtual cstring message() const = 0;
191 bool hasUninitializedParts() const override { return false; }
192
193 DECLARE_TYPEINFO(SymbolicError, SymbolicValue);
194};
195
197 public:
198 const P4::StandardExceptions exc;
199 SymbolicException(const IR::Node *errorPosition, P4::StandardExceptions exc)
200 : SymbolicError(errorPosition), exc(exc) {}
201 SymbolicValue *clone() const override { return new SymbolicException(errorPosition, exc); }
202 void dbprint(std::ostream &out) const override { out << "Exception: " << exc; }
203 cstring message() const override {
204 std::stringstream str;
205 str << exc;
206 return str.str();
207 }
208 bool equals(const SymbolicValue *other) const override;
209
210 DECLARE_TYPEINFO(SymbolicException, SymbolicError);
211};
212
214 public:
215 const std::string msg;
216 SymbolicStaticError(const IR::Node *errorPosition, std::string_view message)
217 : SymbolicError(errorPosition), msg(message) {}
218 SymbolicValue *clone() const override { return new SymbolicStaticError(errorPosition, msg); }
219 void dbprint(std::ostream &out) const override { out << "Error: " << msg; }
220 cstring message() const override { return msg; }
221 bool equals(const SymbolicValue *other) const override;
222
223 DECLARE_TYPEINFO(SymbolicStaticError, SymbolicError);
224};
225
227 public:
228 enum class ValueState {
229 Uninitialized,
230 NotConstant, // we cannot tell statically
231 Constant // compile-time constant
232 };
233
234 protected:
235 ScalarValue(ScalarValue::ValueState state, const IR::Type *type)
236 : SymbolicValue(type), state(state) {}
237
238 public:
239 ValueState state;
240 bool isUninitialized() const { return state == ValueState::Uninitialized; }
241 bool isUnknown() const { return state == ValueState::NotConstant; }
242 bool isKnown() const { return state == ValueState::Constant; }
243 bool isScalar() const override { return true; }
244 void dbprint(std::ostream &out) const override {
245 if (isUninitialized())
246 out << "uninitialized";
247 else if (isUnknown())
248 out << "unknown";
249 }
250 static ValueState init(bool uninit) {
251 return uninit ? ValueState::Uninitialized : ValueState::NotConstant;
252 }
253 void setAllUnknown() override { state = ScalarValue::ValueState::NotConstant; }
254 ValueState mergeState(ValueState other) const {
255 if (state == ValueState::Uninitialized && other == ValueState::Uninitialized)
256 return ValueState::Uninitialized;
257 if (state == ValueState::Constant && other == ValueState::Constant)
258 // This may be wrong.
259 return ValueState::Constant;
260 return ValueState::NotConstant;
261 }
262 bool hasUninitializedParts() const override { return state == ValueState::Uninitialized; }
263
264 DECLARE_TYPEINFO(ScalarValue, SymbolicValue);
265};
266
268 SymbolicVoid() : SymbolicValue(IR::Type_Void::get()) {}
269 static SymbolicVoid *instance;
270
271 public:
272 void dbprint(std::ostream &out) const override { out << "void"; }
273 void setAllUnknown() override {}
274 bool isScalar() const override { return false; }
275 void assign(const SymbolicValue *) override { BUG("assign to void"); }
276 static SymbolicVoid *get() { return instance; }
277 SymbolicValue *clone() const override { return instance; }
278 bool merge(const SymbolicValue *other) override {
279 BUG_CHECK(other->is<SymbolicVoid>(), "%1%: expected void", other);
280 return false;
281 }
282 bool equals(const SymbolicValue *other) const override { return other == instance; }
283 bool hasUninitializedParts() const override { return false; }
284
285 DECLARE_TYPEINFO(SymbolicVoid, SymbolicValue);
286};
287
288class SymbolicBool final : public ScalarValue {
289 public:
290 bool value;
291 explicit SymbolicBool(ScalarValue::ValueState state)
292 : ScalarValue(state, IR::Type_Boolean::get()), value(false) {}
294 : ScalarValue(ScalarValue::ValueState::Uninitialized, IR::Type_Boolean::get()),
295 value(false) {}
296 explicit SymbolicBool(const IR::BoolLiteral *constant)
297 : ScalarValue(ScalarValue::ValueState::Constant, IR::Type_Boolean::get()),
298 value(constant->value) {}
299 SymbolicBool(const SymbolicBool &other) = default;
300 explicit SymbolicBool(bool value)
301 : ScalarValue(ScalarValue::ValueState::Constant, IR::Type_Boolean::get()), value(value) {}
302 void dbprint(std::ostream &out) const override {
303 ScalarValue::dbprint(out);
304 if (!isKnown()) return;
305 out << (value ? "true" : "false");
306 }
307 SymbolicValue *clone() const override {
308 auto result = new SymbolicBool();
309 result->state = state;
310 result->value = value;
311 return result;
312 }
313 void assign(const SymbolicValue *other) override;
314 bool merge(const SymbolicValue *other) override;
315 bool equals(const SymbolicValue *other) const override;
316
317 DECLARE_TYPEINFO(SymbolicBool, ScalarValue);
318};
319
320class SymbolicInteger final : public ScalarValue {
321 public:
322 const IR::Constant *constant;
323 explicit SymbolicInteger(const IR::Type_Bits *type)
324 : ScalarValue(ScalarValue::ValueState::Uninitialized, type), constant(nullptr) {}
325 SymbolicInteger(ScalarValue::ValueState state, const IR::Type_Bits *type)
326 : ScalarValue(state, type), constant(nullptr) {}
327 explicit SymbolicInteger(const IR::Constant *constant)
328 : ScalarValue(ScalarValue::ValueState::Constant, constant->type), constant(constant) {
329 CHECK_NULL(constant);
330 }
331 SymbolicInteger(const SymbolicInteger &other) = default;
332 void dbprint(std::ostream &out) const override {
333 ScalarValue::dbprint(out);
334 if (isKnown()) out << constant->value;
335 }
336 SymbolicValue *clone() const override {
337 auto result = new SymbolicInteger(type->to<IR::Type_Bits>());
338 result->state = state;
339 result->constant = constant;
340 return result;
341 }
342 void assign(const SymbolicValue *other) override;
343 bool merge(const SymbolicValue *other) override;
344 bool equals(const SymbolicValue *other) const override;
345
346 DECLARE_TYPEINFO(SymbolicInteger, ScalarValue);
347};
348
349class SymbolicVarbit final : public ScalarValue {
350 public:
351 explicit SymbolicVarbit(const IR::Type_Varbits *type)
352 : ScalarValue(ScalarValue::ValueState::Uninitialized, type) {}
353 SymbolicVarbit(ScalarValue::ValueState state, const IR::Type_Varbits *type)
354 : ScalarValue(state, type) {}
355 SymbolicVarbit(const SymbolicVarbit &other) = default;
356 void dbprint(std::ostream &out) const override { ScalarValue::dbprint(out); }
357 SymbolicValue *clone() const override {
358 return new SymbolicVarbit(state, type->to<IR::Type_Varbits>());
359 }
360 void assign(const SymbolicValue *other) override;
361 bool merge(const SymbolicValue *other) override;
362 bool equals(const SymbolicValue *other) const override;
363
364 DECLARE_TYPEINFO(SymbolicVarbit, ScalarValue);
365};
366
367// represents enum, error, and match_kind
368class SymbolicEnum final : public ScalarValue {
369 IR::ID value;
370
371 public:
372 explicit SymbolicEnum(const IR::Type *type)
373 : ScalarValue(ScalarValue::ValueState::Uninitialized, type) {}
374 SymbolicEnum(ScalarValue::ValueState state, const IR::Type *type, const IR::ID value)
375 : ScalarValue(state, type), value(value) {}
376 SymbolicEnum(const IR::Type *type, const IR::ID value)
377 : ScalarValue(ScalarValue::ValueState::Constant, type), value(value) {}
378 SymbolicEnum(const SymbolicEnum &other) = default;
379 void dbprint(std::ostream &out) const override {
380 ScalarValue::dbprint(out);
381 if (isKnown()) out << value;
382 }
383 SymbolicValue *clone() const override { return new SymbolicEnum(state, type, value); }
384 void assign(const SymbolicValue *other) override;
385 bool merge(const SymbolicValue *other) override;
386 bool equals(const SymbolicValue *other) const override;
387
388 DECLARE_TYPEINFO(SymbolicEnum, ScalarValue);
389};
390
392 public:
393 explicit SymbolicStruct(const IR::Type_StructLike *type) : SymbolicValue(type) {
394 CHECK_NULL(type);
395 }
396 std::map<cstring, SymbolicValue *> fieldValue;
397 SymbolicStruct(const IR::Type_StructLike *type, bool uninitialized,
398 const SymbolicValueFactory *factory);
399 virtual SymbolicValue *get(const IR::Node *, cstring field) const {
400 auto r = ::P4::get(fieldValue, field);
401 CHECK_NULL(r);
402 return r;
403 }
404 void set(cstring field, SymbolicValue *value) {
405 CHECK_NULL(value);
406 fieldValue[field] = value;
407 }
408 void dbprint(std::ostream &out) const override;
409 bool isScalar() const override { return false; }
410 SymbolicValue *clone() const override;
411 void setAllUnknown() override;
412 void assign(const SymbolicValue *other) override;
413 bool merge(const SymbolicValue *other) override;
414 bool equals(const SymbolicValue *other) const override;
415 bool hasUninitializedParts() const override;
416
417 DECLARE_TYPEINFO(SymbolicStruct, SymbolicValue);
418};
419
421 public:
422 explicit SymbolicHeader(const IR::Type_Header *type) : SymbolicStruct(type) {}
423 SymbolicBool *valid = nullptr;
424 SymbolicHeader(const IR::Type_Header *type, bool uninitialized,
425 const SymbolicValueFactory *factory);
426 virtual void setValid(bool v);
427 SymbolicValue *clone() const override;
428 SymbolicValue *get(const IR::Node *node, cstring field) const override;
429 void setAllUnknown() override;
430 void assign(const SymbolicValue *other) override;
431 void dbprint(std::ostream &out) const override;
432 bool merge(const SymbolicValue *other) override;
433 bool equals(const SymbolicValue *other) const override;
434
435 DECLARE_TYPEINFO(SymbolicHeader, SymbolicStruct);
436};
437
439 public:
440 explicit SymbolicHeaderUnion(const IR::Type_HeaderUnion *type) : SymbolicStruct(type) {}
441 SymbolicHeaderUnion(const IR::Type_HeaderUnion *type, bool uninitialized,
442 const SymbolicValueFactory *factory);
443 SymbolicBool *isValid() const;
444 SymbolicValue *clone() const override;
445 SymbolicValue *get(const IR::Node *node, cstring field) const override;
446 void setAllUnknown() override;
447 void assign(const SymbolicValue *other) override;
448 void dbprint(std::ostream &out) const override;
449 bool merge(const SymbolicValue *other) override;
450 bool equals(const SymbolicValue *other) const override;
451
452 DECLARE_TYPEINFO(SymbolicHeaderUnion, SymbolicStruct);
453};
454
455class SymbolicArray final : public SymbolicValue {
456 std::vector<SymbolicStruct *> values;
457 friend class AnyElement;
458 explicit SymbolicArray(const IR::Type_Stack *type)
459 : SymbolicValue(type),
460 size(type->getSize()),
461 elemType(type->elementType->to<IR::Type_Header>()) {}
462
463 public:
464 const size_t size;
465 const IR::Type_Header *elemType;
466 SymbolicArray(const IR::Type_Stack *stack, bool uninitialized,
467 const SymbolicValueFactory *factory);
468 SymbolicValue *get(const IR::Node *node, size_t index) const {
469 if (index >= values.size())
470 return new SymbolicException(node, P4::StandardExceptions::StackOutOfBounds);
471 return values.at(index);
472 }
473 void shift(int amount); // negative = shift left
474 void set(size_t index, SymbolicHeader *value) {
475 CHECK_NULL(value);
476 values[index] = value;
477 }
478 void dbprint(std::ostream &out) const override;
479 SymbolicValue *clone() const override;
480 SymbolicValue *next(const IR::Node *node);
481 SymbolicValue *last(const IR::Node *node);
482 SymbolicValue *lastIndex(const IR::Node *node);
483 bool isScalar() const override { return false; }
484 void setAllUnknown() override;
485 void assign(const SymbolicValue *other) override;
486 bool merge(const SymbolicValue *other) override;
487 bool equals(const SymbolicValue *other) const override;
488 bool hasUninitializedParts() const override;
489
490 DECLARE_TYPEINFO(SymbolicArray, SymbolicValue);
491};
492
493// Represents any element from a stack
494class AnyElement final : public SymbolicHeader {
495 SymbolicArray *parent;
496
497 public:
498 explicit AnyElement(SymbolicArray *parent) : SymbolicHeader(parent->elemType), parent(parent) {
499 CHECK_NULL(parent);
500 valid = new SymbolicBool();
501 }
502 SymbolicValue *clone() const override {
503 auto result = new AnyElement(parent);
504 return result;
505 }
506 void setAllUnknown() override { parent->setAllUnknown(); }
507 void assign(const SymbolicValue *) override { parent->setAllUnknown(); }
508 void dbprint(std::ostream &out) const override { out << "Any element of " << parent; }
509 void setValid(bool) override { parent->setAllUnknown(); }
510 bool merge(const SymbolicValue *other) override;
511 bool equals(const SymbolicValue *other) const override;
512 SymbolicValue *collapse() const;
513 bool hasUninitializedParts() const override { BUG("Should not be called"); }
514
515 DECLARE_TYPEINFO(AnyElement, SymbolicHeader);
516};
517
518class SymbolicTuple final : public SymbolicValue {
519 std::vector<SymbolicValue *> values;
520
521 public:
522 explicit SymbolicTuple(const IR::Type_Tuple *type) : SymbolicValue(type) {}
523 SymbolicTuple(const IR::Type_Tuple *type, bool uninitialized,
524 const SymbolicValueFactory *factory);
525 SymbolicValue *get(size_t index) const { return values.at(index); }
526 void dbprint(std::ostream &out) const override {
527 bool first = true;
528 for (auto f : values) {
529 if (!first) out << ", ";
530 out << f;
531 first = false;
532 }
533 }
534 SymbolicValue *clone() const override;
535 bool isScalar() const override { return false; }
536 void setAllUnknown() override;
537 void assign(const SymbolicValue *) override { BUG("%1%: tuples are read-only", this); }
538 void add(SymbolicValue *value) { values.push_back(value); }
539 bool merge(const SymbolicValue *other) override;
540 bool equals(const SymbolicValue *other) const override;
541 bool hasUninitializedParts() const override;
542
543 DECLARE_TYPEINFO(SymbolicTuple, SymbolicValue);
544};
545
546// Some extern value of an unknown type
548 public:
549 explicit SymbolicExtern(const IR::Type_Extern *type) : SymbolicValue(type) { CHECK_NULL(type); }
550 void dbprint(std::ostream &out) const override { out << "instance of " << type; }
551 SymbolicValue *clone() const override {
552 return new SymbolicExtern(type->to<IR::Type_Extern>());
553 }
554 bool isScalar() const override { return false; }
555 void setAllUnknown() override { BUG("%1%: extern is read-only", this); }
556 void assign(const SymbolicValue *) override { BUG("%1%: extern is read-only", this); }
557 bool merge(const SymbolicValue *) override { return false; }
558 bool equals(const SymbolicValue *other) const override;
559 bool hasUninitializedParts() const override { return false; }
560
561 DECLARE_TYPEINFO(SymbolicExtern, SymbolicValue);
562};
563
564// Models an extern of type packet_in
565class SymbolicPacketIn final : public SymbolicExtern {
566 // Minimum offset in the stream.
567 // Extracting to a varbit may advance the stream offset
568 // by an unknown quantity. Varbits are counted as 0
569 // (as per SymbolicValueFactory::getWidth).
570 unsigned minimumStreamOffset;
571 // If true the minimumStreamOffset is a conservative
572 // approximation.
573 bool conservative;
574
575 public:
576 explicit SymbolicPacketIn(const IR::Type_Extern *type)
577 : SymbolicExtern(type), minimumStreamOffset(0), conservative(false) {}
578 void dbprint(std::ostream &out) const override {
579 out << "packet_in; offset =" << minimumStreamOffset
580 << (conservative ? " (conservative)" : "");
581 }
582 SymbolicValue *clone() const override {
583 auto result = new SymbolicPacketIn(type->to<IR::Type_Extern>());
584 result->minimumStreamOffset = minimumStreamOffset;
585 result->conservative = conservative;
586 return result;
587 }
588 void setConservative() { conservative = true; }
589 bool isConservative() const { return conservative; }
590 void advance(unsigned width) { minimumStreamOffset += width; }
591 bool merge(const SymbolicValue *other) override;
592 bool equals(const SymbolicValue *other) const override;
593
594 DECLARE_TYPEINFO(SymbolicPacketIn, SymbolicExtern);
595};
596
597} // namespace P4
598
599#endif /* MIDEND_INTERPRETER_H_ */
Definition interpreter.h:494
Definition interpreter.h:124
Definition castable.h:36
Definition stringify.h:33
The Declaration interface, representing objects with names.
Definition declaration.h:26
Definition node.h:95
Definition visitor.h:400
Class used to encode maps from paths to declarations.
Definition referenceMap.h:66
Definition interpreter.h:226
Definition interpreter.h:455
Definition interpreter.h:288
Definition interpreter.h:368
Definition interpreter.h:178
Definition interpreter.h:196
Definition interpreter.h:547
Definition interpreter.h:420
Definition interpreter.h:438
Definition interpreter.h:320
Definition interpreter.h:565
Definition interpreter.h:213
Definition interpreter.h:391
Definition interpreter.h:518
Definition interpreter.h:55
Definition interpreter.h:31
Definition interpreter.h:349
Definition interpreter.h:267
Definition typeMap.h:41
Definition interpreter.h:71
Definition cstring.h:85
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
Definition id.h:28
bool is() const noexcept
Definition rtti.h:216