P4C
The P4 Compiler
Loading...
Searching...
No Matches
typeChecker.h
1/*
2Copyright 2013-present Barefoot Networks, Inc.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#ifndef TYPECHECKING_TYPECHECKER_H_
18#define TYPECHECKING_TYPECHECKER_H_
19
20#include "frontends/common/resolveReferences/referenceMap.h"
21#include "frontends/common/resolveReferences/resolveReferences.h"
22#include "frontends/p4/typeMap.h"
23#include "ir/ir.h"
24#include "ir/pass_manager.h"
25#include "ir/visitor.h"
26
27namespace P4 {
28
29// This pass only clears the typeMap if the program has changed
30// or the 'force' flag is set.
31// This is needed if the types of some objects in the program change.
32class ClearTypeMap : public Inspector {
33 TypeMap *typeMap;
34 bool force;
35
36 public:
37 explicit ClearTypeMap(TypeMap *typeMap, bool force = false) : typeMap(typeMap), force(force) {
38 CHECK_NULL(typeMap);
39 }
40 bool preorder(const IR::P4Program *program) override {
41 // Clear map only if program has not changed from last time
42 // otherwise we can reuse it. The 'force' flag is needed
43 // because the program is saved only *after* typechecking,
44 // so if the program changes during type-checking, the
45 // typeMap may not be complete.
46 if (force || !typeMap->checkMap(program)) typeMap->clear();
47 return false; // prune()
48 }
49};
50
55class TypeChecking : public PassManager {
56 public:
57 TypeChecking(/* out */ ReferenceMap *refMap, /* out */ TypeMap *typeMap,
58 bool updateExpressions = false);
59};
60
61template <typename... Args>
62void typeError(const char *format, Args &&...args) {
63 ::P4::error(ErrorType::ERR_TYPE_ERROR, format, std::forward<Args>(args)...);
64}
66bool hasVarbitsOrUnions(const TypeMap *typeMap, const IR::Type *type);
67
68// Actual type checking algorithm.
69// In general this pass should not be called directly; call TypeChecking instead.
70// It is a transform because it may convert implicit casts into explicit casts.
71// But in general it operates like an Inspector; in fact, if it is instantiated
72// with readOnly = true, it will assert that the program is not changed.
73// It is expected that once a program has been type-checked and all casts have
74// been inserted it will not need to change ever again during type-checking.
75// In fact, several passes do modify the program such that types are invalidated.
76// For example, enum elimination converts enum values into integers. After such
77// changes the typemap has to be cleared and types must be recomputed from scratch.
78class TypeInference : public Transform, public ResolutionContext {
79 // Output: type map
80 TypeMap *typeMap;
81 const IR::Node *initialNode;
82 std::shared_ptr<MinimalNameGenerator> nameGen;
83
84 TypeInference(TypeMap *typeMap, std::shared_ptr<MinimalNameGenerator> nameGen);
85
86 public:
87 // @param readOnly If true it will assert that it behaves like
88 // an Inspector.
89 explicit TypeInference(TypeMap *typeMap, bool readOnly = false, bool checkArrays = true,
90 bool errorOnNullDecls = false);
91
92 protected:
93 // If true we expect to leave the program unchanged
94 bool readOnly = false;
95 bool checkArrays = true;
96 bool errorOnNullDecls = false;
97 const IR::Type *getType(const IR::Node *element) const;
98 const IR::Type *getTypeType(const IR::Node *element) const;
99 void setType(const IR::Node *element, const IR::Type *type);
100 void setLeftValue(const IR::Expression *expression) { typeMap->setLeftValue(expression); }
101 bool isLeftValue(const IR::Expression *expression) const {
102 return typeMap->isLeftValue(expression) || expression->is<IR::DefaultExpression>();
103 }
104 void setCompileTimeConstant(const IR::Expression *expression) {
105 typeMap->setCompileTimeConstant(expression);
106 }
107 bool isCompileTimeConstant(const IR::Expression *expression) const {
108 return typeMap->isCompileTimeConstant(expression);
109 }
110
111 // This is needed because sometimes we invoke visitors recursively on subtrees explicitly.
112 // (visitDagOnce cannot take care of this).
113 bool done() const;
114
115 TypeVariableSubstitution *unifyBase(bool allowCasts, const IR::Node *errorPosition,
116 const IR::Type *destType, const IR::Type *srcType,
117 std::string_view errorFormat,
118 std::initializer_list<const IR::Node *> errorArgs);
119
123 TypeVariableSubstitution *unifyCast(const IR::Node *errorPosition, const IR::Type *destType,
124 const IR::Type *srcType, std::string_view errorFormat = {},
125 std::initializer_list<const IR::Node *> errorArgs = {}) {
126 return unifyBase(true, errorPosition, destType, srcType, errorFormat, errorArgs);
127 }
128
129 TypeVariableSubstitution *unify(const IR::Node *errorPosition, const IR::Type *destType,
130 const IR::Type *srcType, std::string_view errorFormat = {},
131 std::initializer_list<const IR::Node *> errorArgs = {}) {
132 return unifyBase(false, errorPosition, destType, srcType, errorFormat, errorArgs);
133 }
134
139 const IR::Expression *assignment(const IR::Node *errorPosition, const IR::Type *destType,
140 const IR::Expression *sourceExpression);
141 const IR::SelectCase *matchCase(const IR::SelectExpression *select,
142 const IR::Type_BaseList *selectType,
143 const IR::SelectCase *selectCase, const IR::Type *caseType);
144 bool canCastBetween(const IR::Type *dest, const IR::Type *src) const;
145 bool checkAbstractMethods(const IR::Declaration_Instance *inst, const IR::Type_Extern *type);
146 void addSubstitutions(const TypeVariableSubstitution *tvs);
147
148 const IR::Expression *constantFold(const IR::Expression *expression);
149
153 virtual const IR::Type *canonicalize(const IR::Type *type);
154 template <class Ctor>
155 const IR::Type *canonicalizeFields(const IR::Type_StructLike *type, Ctor constructor);
156 virtual const IR::ParameterList *canonicalizeParameters(const IR::ParameterList *params);
157
158 // various helpers
159 bool onlyBitsOrBitStructs(const IR::Type *type) const;
160 bool containsHeader(const IR::Type *canonType);
161 bool validateFields(const IR::Type *type, std::function<bool(const IR::Type *)> checker) const;
162 const IR::Node *binaryBool(const IR::Operation_Binary *op);
163 const IR::Node *binaryArith(const IR::Operation_Binary *op);
164 const IR::Node *unsBinaryArith(const IR::Operation_Binary *op);
165 const IR::Node *shift(const IR::Operation_Binary *op);
166 const IR::Node *typeSet(const IR::Operation_Binary *op);
167
168 const IR::Type *cloneWithFreshTypeVariables(const IR::IMayBeGenericType *type);
169 std::pair<const IR::Type *, const IR::Vector<IR::Argument> *> containerInstantiation(
170 const IR::Node *node, const IR::Vector<IR::Argument> *args,
171 const IR::IContainer *container);
172 const IR::Expression *actionCall(
173 bool inActionList, // if true this "call" is in the action list of a table
174 const IR::MethodCallExpression *actionCall);
175 std::pair<const IR::Type *, const IR::Vector<IR::Argument> *> checkExternConstructor(
176 const IR::Node *errorPosition, const IR::Type_Extern *ext,
177 const IR::Vector<IR::Argument> *arguments);
178
179 static constexpr bool forbidModules = true;
180 static constexpr bool forbidPackages = true;
181 bool checkParameters(const IR::ParameterList *paramList, bool forbidModules = false,
182 bool forbidPackage = false) const;
183 virtual const IR::Type *setTypeType(const IR::Type *type, bool learn = true);
184
186 const IR::ActionList *currentActionList;
190 const IR::ActionListElement *validateActionInitializer(const IR::Expression *actionCall);
191 bool containsActionEnum(const IR::Type *type) const;
192
195 const IR::Type_Bits *checkUnderlyingEnumType(const IR::Type *enumType);
196
198
199 public:
200 using Transform::postorder;
201 using Transform::preorder;
202
203 static const IR::Type *specialize(const IR::IMayBeGenericType *type,
204 const IR::Vector<IR::Type> *arguments,
205 const Visitor::Context *ctxt);
206 const IR::Node *pruneIfDone(const IR::Node *node) {
207 if (done()) {
208 prune();
209 }
210 return node;
211 }
212 const IR::Node *preorder(IR::Expression *expression) override {
213 return pruneIfDone(expression);
214 }
215 const IR::Node *preorder(IR::Type *type) override { return pruneIfDone(type); }
216
217 struct Comparison {
218 const IR::Expression *left;
219 const IR::Expression *right;
220 };
221
222 // Helper function to handle comparisons
223 bool compare(const IR::Node *errorPosition, const IR::Type *ltype, const IR::Type *rtype,
224 Comparison *compare);
225
226 // do functions pre-order so we can check the prototype
227 // before the returns
228 const IR::Node *preorder(IR::Function *function) override;
229 const IR::Node *preorder(IR::P4Program *program) override;
230 const IR::Node *preorder(IR::Declaration_Instance *decl) override;
231 // check invariants for entire list before checking the entries
232 const IR::Node *preorder(IR::EntriesList *el) override;
233 const IR::Node *preorder(IR::Type_SerEnum *type) override;
234
235 const IR::Node *postorder(IR::Declaration_MatchKind *decl) override;
236 const IR::Node *postorder(IR::Declaration_Variable *decl) override;
237 const IR::Node *postorder(IR::Declaration_Constant *constant) override;
238 const IR::Node *postorder(IR::P4Control *cont) override;
239 const IR::Node *postorder(IR::P4Parser *cont) override;
240 const IR::Node *postorder(IR::Method *method) override;
241
242 const IR::Node *postorder(IR::Type_Type *type) override;
243 const IR::Node *postorder(IR::Type_Table *type) override;
244 const IR::Node *postorder(IR::Type_Error *decl) override;
245 const IR::Node *postorder(IR::Type_InfInt *type) override;
246 const IR::Node *postorder(IR::Type_Method *type) override;
247 const IR::Node *postorder(IR::Type_Action *type) override;
248 const IR::Node *postorder(IR::Type_Name *type) override;
249 const IR::Node *postorder(IR::Type_Base *type) override;
250 const IR::Node *postorder(IR::Type_Var *type) override;
251 const IR::Node *postorder(IR::Type_Enum *type) override;
252 const IR::Node *postorder(IR::Type_Extern *type) override;
253 const IR::Node *postorder(IR::StructField *field) override;
254 const IR::Node *postorder(IR::Type_Header *type) override;
255 const IR::Node *postorder(IR::Type_Stack *type) override;
256 const IR::Node *postorder(IR::Type_Struct *type) override;
257 const IR::Node *postorder(IR::Type_HeaderUnion *type) override;
258 const IR::Node *postorder(IR::Type_Typedef *type) override;
259 const IR::Node *postorder(IR::Type_Specialized *type) override;
260 const IR::Node *postorder(IR::Type_SpecializedCanonical *type) override;
261 const IR::Node *postorder(IR::Type_Tuple *type) override;
262 const IR::Node *postorder(IR::Type_P4List *type) override;
263 const IR::Node *postorder(IR::Type_List *type) override;
264 const IR::Node *postorder(IR::Type_Set *type) override;
265 const IR::Node *postorder(IR::Type_ArchBlock *type) override;
266 const IR::Node *postorder(IR::Type_Newtype *type) override;
267 const IR::Node *postorder(IR::Type_Package *type) override;
268 const IR::Node *postorder(IR::Type_ActionEnum *type) override;
269 const IR::Node *postorder(IR::P4Table *type) override;
270 const IR::Node *postorder(IR::P4Action *type) override;
271 const IR::Node *postorder(IR::P4ValueSet *type) override;
272 const IR::Node *postorder(IR::Key *key) override;
273 const IR::Node *postorder(IR::Entry *e) override;
274
275 const IR::Node *postorder(IR::Dots *expression) override;
276 const IR::Node *postorder(IR::Argument *arg) override;
277 const IR::Node *postorder(IR::SerEnumMember *member) override;
278 const IR::Node *postorder(IR::Parameter *param) override;
279 const IR::Node *postorder(IR::Constant *expression) override;
280 const IR::Node *postorder(IR::BoolLiteral *expression) override;
281 const IR::Node *postorder(IR::StringLiteral *expression) override;
282 const IR::Node *postorder(IR::Operation_Relation *expression) override;
283 const IR::Node *postorder(IR::Concat *expression) override;
284 const IR::Node *postorder(IR::ArrayIndex *expression) override;
285 const IR::Node *postorder(IR::LAnd *expression) override { return binaryBool(expression); }
286 const IR::Node *postorder(IR::LOr *expression) override { return binaryBool(expression); }
287 const IR::Node *postorder(IR::Add *expression) override { return binaryArith(expression); }
288 const IR::Node *postorder(IR::Sub *expression) override { return binaryArith(expression); }
289 const IR::Node *postorder(IR::AddSat *expression) override { return binaryArith(expression); }
290 const IR::Node *postorder(IR::SubSat *expression) override { return binaryArith(expression); }
291 const IR::Node *postorder(IR::Mul *expression) override { return binaryArith(expression); }
292 const IR::Node *postorder(IR::Div *expression) override { return unsBinaryArith(expression); }
293 const IR::Node *postorder(IR::Mod *expression) override { return unsBinaryArith(expression); }
294 const IR::Node *postorder(IR::Shl *expression) override { return shift(expression); }
295 const IR::Node *postorder(IR::Shr *expression) override { return shift(expression); }
296 const IR::Node *postorder(IR::BXor *expression) override { return binaryArith(expression); }
297 const IR::Node *postorder(IR::BAnd *expression) override { return binaryArith(expression); }
298 const IR::Node *postorder(IR::BOr *expression) override { return binaryArith(expression); }
299 const IR::Node *postorder(IR::Mask *expression) override { return typeSet(expression); }
300 const IR::Node *postorder(IR::Range *expression) override { return typeSet(expression); }
301 const IR::Node *postorder(IR::LNot *expression) override;
302 const IR::Node *postorder(IR::Neg *expression) override;
303 const IR::Node *postorder(IR::UPlus *expression) override;
304 const IR::Node *postorder(IR::Cmpl *expression) override;
305 const IR::Node *postorder(IR::Cast *expression) override;
306 const IR::Node *postorder(IR::Mux *expression) override;
307 const IR::Node *postorder(IR::Slice *expression) override;
308 const IR::Node *postorder(IR::PathExpression *expression) override;
309 const IR::Node *postorder(IR::Member *expression) override;
310 const IR::Node *postorder(IR::TypeNameExpression *expression) override;
311 const IR::Node *postorder(IR::ListExpression *expression) override;
312 const IR::Node *postorder(IR::InvalidHeader *expression) override;
313 const IR::Node *postorder(IR::InvalidHeaderUnion *expression) override;
314 const IR::Node *postorder(IR::Invalid *expression) override;
315 const IR::Node *postorder(IR::P4ListExpression *expression) override;
316 const IR::Node *postorder(IR::StructExpression *expression) override;
317 const IR::Node *postorder(IR::HeaderStackExpression *expression) override;
318 const IR::Node *postorder(IR::MethodCallStatement *mcs) override;
319 const IR::Node *postorder(IR::MethodCallExpression *expression) override;
320 const IR::Node *postorder(IR::ConstructorCallExpression *expression) override;
321 const IR::Node *postorder(IR::SelectExpression *expression) override;
322 const IR::Node *postorder(IR::DefaultExpression *expression) override;
323 const IR::Node *postorder(IR::This *expression) override;
324 const IR::Node *postorder(IR::AttribLocal *local) override;
325 const IR::Node *postorder(IR::ActionList *al) override;
326
327 const IR::Node *postorder(IR::ReturnStatement *stat) override;
328 const IR::Node *postorder(IR::IfStatement *stat) override;
329 const IR::Node *postorder(IR::SwitchStatement *stat) override;
330 const IR::Node *postorder(IR::AssignmentStatement *stat) override;
331 const IR::Node *postorder(IR::ForInStatement *stat) override;
332 const IR::Node *postorder(IR::ActionListElement *elem) override;
333 const IR::Node *postorder(IR::KeyElement *elem) override;
334 const IR::Node *postorder(IR::Property *elem) override;
335 const IR::Node *postorder(IR::SelectCase *elem) override;
336 const IR::Node *postorder(IR::Annotation *annotation) override;
337
338 Visitor::profile_t init_apply(const IR::Node *node) override;
339 void end_apply(const IR::Node *Node) override;
340 const IR::Node *apply_visitor(const IR::Node *, const char *name = 0) override;
341
342 TypeInference *clone() const override;
343 // Apply recursively the typechecker to the newly created node
344 // to add all component subtypes in the typemap.
345 // Return 'true' if errors were discovered in the learning process.
346 bool learn(const IR::Node *node, Visitor *caller, const Visitor::Context *ctxt);
347};
348
349// Copy types from the typeMap to expressions. Updates the typeMap with newly created nodes
350class ApplyTypesToExpressions : public Transform {
351 TypeMap *typeMap;
352 IR::Node *postorder(IR::Node *n) override {
353 const IR::Node *orig = getOriginal();
354 if (auto type = typeMap->getType(orig)) {
355 if (*orig != *n) typeMap->setType(n, type);
356 }
357 return n;
358 }
359 IR::Expression *postorder(IR::Expression *e) override {
360 auto orig = getOriginal<IR::Expression>();
361 if (auto type = typeMap->getType(orig)) {
362 e->type = type;
363 if (*orig != *e) {
364 typeMap->setType(e, type);
365 if (typeMap->isLeftValue(orig)) typeMap->setLeftValue(e);
366 if (typeMap->isCompileTimeConstant(orig)) typeMap->setCompileTimeConstant(e);
367 }
368 }
369 return e;
370 }
371
372 public:
373 explicit ApplyTypesToExpressions(TypeMap *typeMap) : typeMap(typeMap) {}
374};
375
376} // namespace P4
377
378#endif /* TYPECHECKING_TYPECHECKER_H_ */
Definition node.h:95
Definition vector.h:58
Definition visitor.h:400
Class used to encode maps from paths to declarations.
Definition referenceMap.h:66
Definition visitor.h:424
const IR::ActionListElement * validateActionInitializer(const IR::Expression *actionCall)
Definition typeCheckStmt.cpp:214
TypeVariableSubstitution * unifyCast(const IR::Node *errorPosition, const IR::Type *destType, const IR::Type *srcType, std::string_view errorFormat={}, std::initializer_list< const IR::Node * > errorArgs={})
Definition typeChecker.h:123
bool validateFields(const IR::Type *type, std::function< bool(const IR::Type *)> checker) const
Definition typeCheckTypes.cpp:494
const IR::ActionList * currentActionList
Action list of the current table.
Definition typeChecker.h:186
std::pair< const IR::Type *, const IR::Vector< IR::Argument > * > containerInstantiation(const IR::Node *node, const IR::Vector< IR::Argument > *args, const IR::IContainer *container)
Definition typeChecker.cpp:845
TypeVariableSubstitution * unify(const IR::Node *errorPosition, const IR::Type *destType, const IR::Type *srcType, std::string_view errorFormat={}, std::initializer_list< const IR::Node * > errorArgs={})
Same as above, not allowing casts.
Definition typeChecker.h:129
const IR::Type_Bits * checkUnderlyingEnumType(const IR::Type *enumType)
Definition typeCheckTypes.cpp:302
static const IR::Type * specialize(const IR::IMayBeGenericType *type, const IR::Vector< IR::Type > *arguments, const Visitor::Context *ctxt)
Definition typeChecker.cpp:217
virtual const IR::Type * canonicalize(const IR::Type *type)
Definition typeChecker.cpp:235
const IR::Expression * assignment(const IR::Node *errorPosition, const IR::Type *destType, const IR::Expression *sourceExpression)
Definition typeChecker.cpp:545
Definition typeChecker.h:217
Definition typeMap.h:41
Definition typeSubstitution.h:73
Definition typeMap.h:41
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition error.h:51
bool hasVarbitsOrUnions(const TypeMap *typeMap, const IR::Type *type)
True if the type contains any varbit or header_union subtypes.
Definition typeCheckTypes.cpp:20