source: src/AST/Util.cpp@ a0b59ed

Last change on this file since a0b59ed was b7c53a9d, checked in by Andrew Beach <ajbeach@…>, 2 years ago

Added a new invariant check and the fixes required to make it pass. Not the new check is by no means exaustive (it doesn't even check every readonly pointer) but it should catch the most common/problematic cases.

  • Property mode set to 100644
File size: 5.0 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
23#include <vector>
24
25namespace ast {
26
27namespace {
28
29/// Check that ast::ptr/strong references do not form a cycle.
30struct NoStrongCyclesCore {
31 std::vector<const Node *> parents;
32
33 void previsit( const Node * node ) {
34 for ( auto & parent : parents ) {
35 assert( parent != node );
36 }
37 parents.push_back( node );
38 }
39
40 void postvisit( const Node * node ) {
41 assert( !parents.empty() );
42 assert( parents.back() == node );
43 parents.pop_back();
44 }
45};
46
47/// Check that every note that can has a set CodeLocation.
48void isCodeLocationSet( const ParseNode * node ) {
49 assert( node->location.isSet() );
50}
51
52void areLabelLocationsSet( const Stmt * stmt ) {
53 for ( const Label& label : stmt->labels ) {
54 assert( label.location.isSet() );
55 }
56}
57
58/// Make sure the reference counts are in a valid combination.
59void isStable( const Node * node ) {
60 assert( node->isStable() );
61}
62
63/// Check that a FunctionDecl is synchronized with it's FunctionType.
64void functionDeclMatchesType( const FunctionDecl * decl ) {
65 // The type is a cache of sorts, if it is missing that is only a
66 // problem if isTypeFixed is set.
67 if ( decl->isTypeFixed ) {
68 assert( decl->type );
69 } else if ( !decl->type ) {
70 return;
71 }
72
73 const FunctionType * type = decl->type;
74
75 // Check that `type->forall` corresponds with `decl->type_params`.
76 assert( type->forall.size() == decl->type_params.size() );
77 // Check that `type->assertions` corresponds with `decl->assertions`.
78 assert( type->assertions.size() == decl->assertions.size() );
79 // Check that `type->params` corresponds with `decl->params`.
80 assert( type->params.size() == decl->params.size() );
81 // Check that `type->returns` corresponds with `decl->returns`.
82 assert( type->returns.size() == decl->returns.size() );
83}
84
85/// Check that the MemberExpr has an aggregate type and matching member.
86void memberMatchesAggregate( const MemberExpr * expr ) {
87 const Type * aggrType = expr->aggregate->result->stripReferences();
88 const AggregateDecl * decl = nullptr;
89 if ( auto inst = dynamic_cast<const StructInstType *>( aggrType ) ) {
90 decl = inst->base;
91 } else if ( auto inst = dynamic_cast<const UnionInstType *>( aggrType ) ) {
92 decl = inst->base;
93 }
94 assertf( decl, "Aggregate of member not correct type." );
95
96 for ( auto aggrMember : decl->members ) {
97 if ( expr->member == aggrMember ) {
98 return;
99 }
100 }
101 assertf( false, "Member not found." );
102}
103
104/// Check for Floating Nodes:
105/// Every node should be reachable from a root (the TranslationUnit) via a
106/// chain of structural references (tracked with ptr). This cannot check all
107/// of that, it just checks if a given node's field has a strong reference.
108template<typename node_t, typename field_t>
109void noFloatingNode( const node_t * node, field_t node_t::*field_ptr ) {
110 const field_t & field = node->*field_ptr;
111 if ( nullptr == field ) return;
112 assertf( field->isManaged(), "Floating node found." );
113}
114
115struct InvariantCore {
116 // To save on the number of visits: this is a kind of composed core.
117 // None of the passes should make changes so ordering doesn't matter.
118 NoStrongCyclesCore no_strong_cycles;
119
120 void previsit( const Node * node ) {
121 no_strong_cycles.previsit( node );
122 isStable( node );
123 }
124
125 void previsit( const ParseNode * node ) {
126 previsit( (const Node *)node );
127 isCodeLocationSet( node );
128 }
129
130 void previsit( const FunctionDecl * node ) {
131 previsit( (const ParseNode *)node );
132 functionDeclMatchesType( node );
133 }
134
135 void previsit( const Stmt * node ) {
136 previsit( (const ParseNode *)node );
137 areLabelLocationsSet( node );
138 }
139
140 void previsit( const VariableExpr * node ) {
141 previsit( (const ParseNode *)node );
142 noFloatingNode( node, &VariableExpr::var );
143 }
144
145 void previsit( const MemberExpr * node ) {
146 previsit( (const ParseNode *)node );
147 memberMatchesAggregate( node );
148 }
149
150 void previsit( const StructInstType * node ) {
151 previsit( (const Node *)node );
152 noFloatingNode( node, &StructInstType::base );
153 }
154
155 void previsit( const UnionInstType * node ) {
156 previsit( (const Node *)node );
157 noFloatingNode( node, &UnionInstType::base );
158 }
159
160 void previsit( const EnumInstType * node ) {
161 previsit( (const Node *)node );
162 noFloatingNode( node, &EnumInstType::base );
163 }
164
165 void previsit( const TypeInstType * node ) {
166 previsit( (const Node *)node );
167 noFloatingNode( node, &TypeInstType::base );
168 }
169
170 void postvisit( const Node * node ) {
171 no_strong_cycles.postvisit( node );
172 }
173};
174
175} // namespace
176
177void checkInvariants( TranslationUnit & transUnit ) {
178 ast::Pass<InvariantCore>::run( transUnit );
179}
180
181} // namespace ast
Note: See TracBrowser for help on using the repository browser.