source: src/AST/Util.cpp @ dd7c2ce0

Last change on this file since dd7c2ce0 was bfeb37a6, checked in by Andrew Beach <ajbeach@…>, 15 months ago

Added another check to the invariants for SizeofExpr/AlignofExpr?.

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