source: src/AST/Util.cpp@ d914750

Last change on this file since d914750 was 17fa94f, checked in by Andrew Beach <ajbeach@…>, 7 months ago

Reworked some nodes so they can be typed or untyped. This allowed me to remove TranslationDeps as the type information is only needed in the candidate finder, which can easily insert it.

  • Property mode set to 100644
File size: 10.5 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// Util.cpp -- General utilities for working with the AST.
8//
9// Author : Andrew Beach
10// Created On : Wed Jan 19 9:46:00 2022
11// Last Modified By : Andrew Beach
12// Last Modified On : Wed May 11 16:16:00 2022
13// Update Count : 3
14//
15
16#include "Util.hpp"
17
18#include "Node.hpp"
19#include "ParseNode.hpp"
20#include "Pass.hpp"
21#include "TranslationUnit.hpp"
22#include "Common/Utility.hpp"
23#include "GenPoly/ScopedSet.hpp"
24#include "Decl.hpp"
25#include "Type.hpp"
26
27#include <vector>
28
29using GenPoly::ScopedSet;
30
31namespace ast {
32
33namespace {
34
35/// Check that ast::ptr/strong references do not form a cycle.
36struct NoStrongCyclesCore {
37 std::vector<const Node *> parents;
38
39 void previsit( const Node * node ) {
40 for ( auto & parent : parents ) {
41 assert( parent != node );
42 }
43 parents.push_back( node );
44 }
45
46 void postvisit( const Node * node ) {
47 assert( !parents.empty() );
48 assert( parents.back() == node );
49 parents.pop_back();
50 }
51};
52
53/// Check that every note that can has a set CodeLocation.
54void isCodeLocationSet( const ParseNode * node ) {
55 assert( node->location.isSet() );
56}
57
58void areLabelLocationsSet( const Stmt * stmt ) {
59 for ( const Label& label : stmt->labels ) {
60 assert( label.location.isSet() );
61 }
62}
63
64/// Make sure the reference counts are in a valid combination.
65void isStable( const Node * node ) {
66 assert( node->isStable() );
67}
68
69/// Check that a FunctionDecl is synchronized with it's FunctionType.
70void functionDeclMatchesType( const FunctionDecl * decl ) {
71 // The type is a cache of sorts, if it is missing that is only a
72 // problem if isTypeFixed is set.
73 if ( decl->isTypeFixed ) {
74 assert( decl->type );
75 } else if ( !decl->type ) {
76 return;
77 }
78
79 const FunctionType * type = decl->type;
80
81 // Check that `type->forall` corresponds with `decl->type_params`.
82 assert( type->forall.size() == decl->type_params.size() );
83 // Check that `type->assertions` corresponds with `decl->assertions`.
84 assert( type->assertions.size() == decl->assertions.size() );
85 // Check that `type->params` corresponds with `decl->params`.
86 assert( type->params.size() == decl->params.size() );
87 // Check that `type->returns` corresponds with `decl->returns`.
88 assert( type->returns.size() == decl->returns.size() );
89}
90
91/// Check that an enumeration has not been made with an inconsistent spec.
92void isEnumerationConsistent( const EnumDecl * node ) {
93 if ( node->is_c_enum() ) {
94 assert( nullptr == node->base );
95 }
96}
97
98/// Check that the MemberExpr has an aggregate type and matching member.
99void memberMatchesAggregate( const MemberExpr * expr ) {
100 const Type * aggrType = expr->aggregate->result->stripReferences();
101 const AggregateDecl * decl = nullptr;
102 if ( auto inst = dynamic_cast<const StructInstType *>( aggrType ) ) {
103 decl = inst->base;
104 } else if ( auto inst = dynamic_cast<const UnionInstType *>( aggrType ) ) {
105 decl = inst->base;
106 }
107 assertf( decl, "Aggregate of member not correct type." );
108
109 for ( auto aggrMember : decl->members ) {
110 if ( expr->member == aggrMember ) {
111 return;
112 }
113 }
114 assertf( false, "Member not found." );
115}
116
117/// Check for Floating Nodes:
118/// Every node should be reachable from a root (the TranslationUnit) via a
119/// chain of structural references (tracked with ptr). This cannot check all
120/// of that, it just checks if a given node's field has a strong reference.
121template<typename node_t, typename field_t>
122void noFloatingNode( const node_t * node, field_t node_t::*field_ptr ) {
123 const field_t & field = node->*field_ptr;
124 if ( nullptr == field ) return;
125 assertf( field->isManaged(), "Floating node found." );
126}
127
128struct InvariantCore {
129 // To save on the number of visits: this is a kind of composed core.
130 // None of the passes should make changes so ordering doesn't matter.
131 NoStrongCyclesCore no_strong_cycles;
132
133 void previsit( const Node * node ) {
134 no_strong_cycles.previsit( node );
135 isStable( node );
136 }
137
138 void previsit( const ParseNode * node ) {
139 previsit( (const Node *)node );
140 isCodeLocationSet( node );
141 }
142
143 void previsit( const FunctionDecl * node ) {
144 previsit( (const ParseNode *)node );
145 functionDeclMatchesType( node );
146 }
147
148 void previsit( const EnumDecl * node ) {
149 previsit( (const ParseNode *)node );
150 isEnumerationConsistent( node );
151 }
152
153 void previsit( const Stmt * node ) {
154 previsit( (const ParseNode *)node );
155 areLabelLocationsSet( node );
156 }
157
158 void previsit( const VariableExpr * node ) {
159 previsit( (const ParseNode *)node );
160 noFloatingNode( node, &VariableExpr::var );
161 }
162
163 void previsit( const MemberExpr * node ) {
164 previsit( (const ParseNode *)node );
165 memberMatchesAggregate( node );
166 }
167
168 void previsit( const StructInstType * node ) {
169 previsit( (const Node *)node );
170 noFloatingNode( node, &StructInstType::base );
171 }
172
173 void previsit( const UnionInstType * node ) {
174 previsit( (const Node *)node );
175 noFloatingNode( node, &UnionInstType::base );
176 }
177
178 void previsit( const EnumInstType * node ) {
179 previsit( (const Node *)node );
180 noFloatingNode( node, &EnumInstType::base );
181 }
182
183 void previsit( const TypeInstType * node ) {
184 previsit( (const Node *)node );
185 noFloatingNode( node, &TypeInstType::base );
186 }
187
188 void postvisit( const Node * node ) {
189 no_strong_cycles.postvisit( node );
190 }
191};
192
193/// Checks that referred to nodes are in scope.
194/// This checks many readonly pointers to see if the declaration they are
195/// referring to is in scope by the structural rules of code.
196// Any escapes marked with a bug should be removed once the bug is fixed.
197// This is a separate pass because of it changes the visit pattern and
198// must always be run on the entire translation unit.
199struct InScopeCore : public ast::WithShortCircuiting {
200 ScopedSet<DeclWithType const *> typedDecls;
201 ScopedSet<TypeDecl const *> typeDecls;
202 // These 3 are really hard to check, because uses that originally ref. at
203 // a forward declaration can be rewired to point a later full definition.
204 ScopedSet<StructDecl const *> structDecls;
205 ScopedSet<UnionDecl const *> unionDecls;
206 ScopedSet<EnumDecl const *> enumDecls;
207 ScopedSet<TraitDecl const *> traitDecls;
208
209 bool isInGlobal = false;
210
211 void beginScope() {
212 typedDecls.beginScope();
213 typeDecls.beginScope();
214 structDecls.beginScope();
215 unionDecls.beginScope();
216 enumDecls.beginScope();
217 traitDecls.beginScope();
218 }
219
220 void endScope() {
221 typedDecls.endScope();
222 typeDecls.endScope();
223 structDecls.endScope();
224 unionDecls.endScope();
225 enumDecls.endScope();
226 traitDecls.endScope();
227 }
228
229 void previsit( ApplicationExpr const * expr ) {
230 // All isInGlobal manipulation is just to isolate this check.
231 // The invalid compound literals lead to bad ctor/dtors. [#280]
232 VariableExpr const * func = nullptr;
233 CastExpr const * cast = nullptr;
234 VariableExpr const * arg = nullptr;
235 if ( isInGlobal
236 && 1 == expr->args.size()
237 && ( func = expr->func.as<VariableExpr>() )
238 && ( "?{}" == func->var->name || "^?{}" == func->var->name )
239 && ( cast = expr->args[0].as<CastExpr>() )
240 && ( arg = cast->arg.as<VariableExpr>() )
241 && isPrefix( arg->var->name, "_compLit" ) ) {
242 visit_children = false;
243 }
244 }
245
246 void previsit( VariableExpr const * expr ) {
247 if ( !expr->var ) return;
248 // bitwise assignment escape [#281]
249 if ( expr->var->location.isUnset() ) return;
250 assert( typedDecls.contains( expr->var ) );
251 }
252
253 void previsit( FunctionType const * type ) {
254 // This is to avoid checking the assertions, which can point at the
255 // function's declaration and not the enclosing function.
256 for ( auto type_param : type->forall ) {
257 if ( type_param->formal_usage ) {
258 visit_children = false;
259 // We could check non-assertion fields here.
260 }
261 }
262 }
263
264 void previsit( TypeInstType const * type ) {
265 if ( !type->base ) return;
266 assertf( type->base->isManaged(), "Floating Node" );
267
268 // bitwise assignment escape [#281]
269 if ( type->base->location.isUnset() ) return;
270 // Formal types can actually look at out of scope variables.
271 if ( type->formal_usage ) return;
272 assert( typeDecls.contains( type->base ) );
273 }
274
275 void previsit( TraitInstType const * type ) {
276 if ( !type->base ) return;
277 assert( traitDecls.contains( type->base ) );
278 }
279
280 void previsit( ObjectDecl const * decl ) {
281 typedDecls.insert( decl );
282 // There are some ill-formed compound literals. [#280]
283 // The only known problem cases are at the top level.
284 if ( isPrefix( decl->name, "_compLit" ) ) {
285 visit_children = false;
286 }
287 }
288
289 void previsit( FunctionDecl const * decl ) {
290 typedDecls.insert( decl );
291 beginScope();
292 for ( auto & type_param : decl->type_params ) {
293 typeDecls.insert( type_param );
294 }
295 for ( auto & assertion : decl->assertions ) {
296 typedDecls.insert( assertion );
297 }
298 for ( auto & param : decl->params ) {
299 typedDecls.insert( param );
300 }
301 for ( auto & ret : decl->returns ) {
302 typedDecls.insert( ret );
303 }
304 // No special handling of withExprs.
305
306 // Part of the compound literal escape. [#280]
307 if ( "__global_init__" == decl->name
308 || "__global_destroy__" == decl->name ) {
309 assert( !isInGlobal );
310 isInGlobal = true;
311 }
312 }
313
314 void postvisit( FunctionDecl const * decl ) {
315 endScope();
316 // Part of the compound literal escape. [#280]
317 if ( isInGlobal && ( "__global_init__" == decl->name
318 || "__global_destroy__" == decl->name ) ) {
319 isInGlobal = false;
320 }
321 }
322
323 void previsit( StructDecl const * decl ) {
324 structDecls.insert( decl );
325 beginScope();
326 for ( auto & type_param : decl->params ) {
327 typeDecls.insert( type_param );
328 }
329 }
330
331 void postvisit( StructDecl const * ) {
332 endScope();
333 }
334
335 void previsit( UnionDecl const * decl ) {
336 unionDecls.insert( decl );
337 beginScope();
338 for ( auto & type_param : decl->params ) {
339 typeDecls.insert( type_param );
340 }
341 }
342
343 void postvisit( UnionDecl const * ) {
344 endScope();
345 }
346
347 void previsit( EnumDecl const * decl ) {
348 enumDecls.insert( decl );
349 for ( auto & member : decl->members ) {
350 typedDecls.insert( member.strict_as<ast::DeclWithType>() );
351 }
352 beginScope();
353 for ( auto & type_param : decl->params ) {
354 typeDecls.insert( type_param );
355 }
356 }
357
358 void postvisit( EnumDecl const * ) {
359 endScope();
360 }
361
362 void previsit( TraitDecl const * decl ) {
363 traitDecls.insert( decl );
364 beginScope();
365 for ( auto & type_param : decl->params ) {
366 typeDecls.insert( type_param );
367 }
368 }
369
370 void postvisit( TraitDecl const * ) {
371 endScope();
372 }
373
374 void previsit( Designation const * ) {
375 visit_children = false;
376 }
377};
378
379} // namespace
380
381void checkInvariants( TranslationUnit & transUnit ) {
382 Pass<InvariantCore>::run( transUnit );
383 Pass<InScopeCore>::run( transUnit );
384}
385
386} // namespace ast
Note: See TracBrowser for help on using the repository browser.