source: src/AST/Util.cpp@ b28ce93

Last change on this file since b28ce93 was 119889f, checked in by Michael Brooks <mlbrooks@…>, 6 months ago

Partially fix #185.

This fix applies to functions, but not types.

The added test fails without the cfacpp changes.

  • Property mode set to 100644
File size: 10.6 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 if ( ! type->assertions.empty() ) visit_children = false;
263 }
264
265 void previsit( TypeInstType const * type ) {
266 if ( !type->base ) return;
267 assertf( type->base->isManaged(), "Floating Node" );
268
269 // bitwise assignment escape [#281]
270 if ( type->base->location.isUnset() ) return;
271 // Formal types can actually look at out of scope variables.
272 if ( type->formal_usage ) return;
273 assert( typeDecls.contains( type->base ) );
274 }
275
276 void previsit( TraitInstType const * type ) {
277 if ( !type->base ) return;
278 assert( traitDecls.contains( type->base ) );
279 }
280
281 void previsit( ObjectDecl const * decl ) {
282 typedDecls.insert( decl );
283 // There are some ill-formed compound literals. [#280]
284 // The only known problem cases are at the top level.
285 if ( isPrefix( decl->name, "_compLit" ) ) {
286 visit_children = false;
287 }
288 }
289
290 void previsit( FunctionDecl const * decl ) {
291 typedDecls.insert( decl );
292 beginScope();
293 for ( auto & type_param : decl->type_params ) {
294 typeDecls.insert( type_param );
295 }
296 for ( auto & assertion : decl->assertions ) {
297 typedDecls.insert( assertion );
298 }
299 for ( auto & param : decl->params ) {
300 typedDecls.insert( param );
301 }
302 for ( auto & ret : decl->returns ) {
303 typedDecls.insert( ret );
304 }
305 // No special handling of withExprs.
306
307 // Part of the compound literal escape. [#280]
308 if ( "__global_init__" == decl->name
309 || "__global_destroy__" == decl->name ) {
310 assert( !isInGlobal );
311 isInGlobal = true;
312 }
313 }
314
315 void postvisit( FunctionDecl const * decl ) {
316 endScope();
317 // Part of the compound literal escape. [#280]
318 if ( isInGlobal && ( "__global_init__" == decl->name
319 || "__global_destroy__" == decl->name ) ) {
320 isInGlobal = false;
321 }
322 }
323
324 void previsit( StructDecl const * decl ) {
325 structDecls.insert( decl );
326 beginScope();
327 for ( auto & type_param : decl->params ) {
328 typeDecls.insert( type_param );
329 }
330 }
331
332 void postvisit( StructDecl const * ) {
333 endScope();
334 }
335
336 void previsit( UnionDecl const * decl ) {
337 unionDecls.insert( decl );
338 beginScope();
339 for ( auto & type_param : decl->params ) {
340 typeDecls.insert( type_param );
341 }
342 }
343
344 void postvisit( UnionDecl const * ) {
345 endScope();
346 }
347
348 void previsit( EnumDecl const * decl ) {
349 enumDecls.insert( decl );
350 for ( auto & member : decl->members ) {
351 typedDecls.insert( member.strict_as<ast::DeclWithType>() );
352 }
353 beginScope();
354 for ( auto & type_param : decl->params ) {
355 typeDecls.insert( type_param );
356 }
357 }
358
359 void postvisit( EnumDecl const * ) {
360 endScope();
361 }
362
363 void previsit( TraitDecl const * decl ) {
364 traitDecls.insert( decl );
365 beginScope();
366 for ( auto & type_param : decl->params ) {
367 typeDecls.insert( type_param );
368 }
369 }
370
371 void postvisit( TraitDecl const * ) {
372 endScope();
373 }
374
375 void previsit( Designation const * ) {
376 visit_children = false;
377 }
378};
379
380} // namespace
381
382void checkInvariants( TranslationUnit & transUnit ) {
383 Pass<InvariantCore>::run( transUnit );
384 Pass<InScopeCore>::run( transUnit );
385}
386
387} // namespace ast
Note: See TracBrowser for help on using the repository browser.