source: src/AST/Util.cpp @ b5978ca

Last change on this file since b5978ca was 17fa94f, checked in by Andrew Beach <ajbeach@…>, 2 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.