source: src/AST/Util.cpp@ a8e2215

Last change on this file since a8e2215 was d3d54b3, checked in by Michael Brooks <mlbrooks@…>, 8 months ago

Eliminate warnings due to sizeof returning type inconsistent with %zu. Fix #269.

Test

nowarn/printf-sizeof

: Activate direct test of the bug.

Tests

alloc-ERROR
extension
array-collections/dimexpr-match-c-ERRS

: Accommodate incidental changes in error-message text or generated code.

  • Property mode set to 100644
File size: 11.3 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
386namespace {
387 const TranslationUnit * transUnit = 0;
388}
389
390void TranslationDeps::evolve( TranslationUnit & u ) {
391 transUnit = &u;
392}
393
394const ast::Type * TranslationDeps::getSizeType() {
395 static const ast::Type * zd_abstract = new TypeInstType{ "size_t", TypeDecl::Kind::Dtype };
396 static const ast::Type * ld_concrete = new BasicType( BasicKind::LongUnsignedInt );
397 if ( ! transUnit ) {
398 // early state
399 // as if `size_t` in program text were freshly parsed
400 return zd_abstract;
401 } else if ( transUnit->global.sizeType ) {
402 // late state, normal run
403 // whatever size_t was defined as
404 return transUnit->global.sizeType;
405 } else {
406 // late state, no prelude (-n)
407 // placeholder: cfa-cpp is being used experimentally, stay out of the way
408 return ld_concrete;
409 }
410}
411
412
413} // namespace ast
Note: See TracBrowser for help on using the repository browser.