source: src/AST/Pass.proto.hpp@ 9131e54

ADT arm-eh ast-experimental cleanup-dtors enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since 9131e54 was f47f887, checked in by Thierry Delisle <tdelisle@…>, 6 years ago

First draft of Pass.hpp and some updates to node.hpp

  • Property mode set to 100644
File size: 10.5 KB
Line 
1#pragma once
2// IWYU pragma: private, include "Pass.hpp"
3
4namespace ast {
5 template<typename pass_type>
6 class Pass;
7
8 namespace __pass {
9 typedef std::function<void( void * )> cleanup_func_t;
10 typedef std::function<void( cleanup_func_t, void * )> at_cleanup_t;
11
12
13 // boolean reference that may be null
14 // either refers to a boolean value or is null and returns true
15 class bool_ref {
16 public:
17 bool_ref() = default;
18 ~bool_ref() = default;
19
20 operator bool() { return m_ref ? *m_ref : true; }
21 bool operator=( bool val ) { assert(m_ref); return *m_ref = val; }
22
23 private:
24
25 friend class visit_children_guard;
26
27 bool * set( bool * val ) {
28 bool * prev = m_ref;
29 m_ref = val;
30 return prev;
31 }
32
33 bool * m_ref = nullptr;
34 };
35
36 // Implementation of the guard value
37 // Created inside the visit scope
38 class guard_value {
39 public:
40 guard_value( at_cleanup_t * at_cleanup ) {
41 if( at_cleanup ) {
42 *at_cleanup = [this]( cleanup_func_t && func, void* val ) {
43 push( std::move( func ), val );
44 };
45 }
46 }
47
48 ~guard_value() {
49 while( !cleanups.empty() ) {
50 auto& cleanup = cleanups.top();
51 cleanup.func( cleanup.val );
52 cleanups.pop();
53 }
54 }
55
56 void push( cleanup_func_t && func, void* val ) {
57 cleanups.emplace( std::move(func), val );
58 }
59
60 private:
61 struct cleanup_t {
62 cleanup_func_t func;
63 void * val;
64
65 cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {}
66 };
67
68 std::stack< cleanup_t > cleanups;
69 };
70
71 // Guard structure implementation for whether or not children should be visited
72 class visit_children_guard {
73 public:
74
75 visit_children_guard( bool_ref * ref )
76 : m_val ( true )
77 , m_prev( ref ? ref->set( &m_val ) : nullptr )
78 , m_ref ( ref )
79 {}
80
81 ~visit_children_guard() {
82 if( m_ref ) {
83 m_ref->set( m_prev );
84 }
85 }
86
87 operator bool() { return m_val; }
88
89 private:
90 bool m_val;
91 bool * m_prev;
92 bool_ref * m_ref;
93 };
94
95 //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
96 // Deep magic (a.k.a template meta programming) to make the templated visitor work
97 // Basically the goal is to make 2 previsit
98 // 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of
99 // 'pass.previsit( node )' that compiles will be used for that node for that type
100 // This requires that this option only compile for passes that actually define an appropriate visit.
101 // SFINAE will make sure the compilation errors in this function don't halt the build.
102 // See http://en.cppreference.com/w/cpp/language/sfinae for details on SFINAE
103 // 2 - Since the first implementation might not be specilizable, the second implementation exists and does nothing.
104 // This is needed only to eliminate the need for passes to specify any kind of handlers.
105 // The second implementation only works because it has a lower priority. This is due to the bogus last parameter.
106 // The second implementation takes a long while the first takes an int. Since the caller always passes an literal 0
107 // the first implementation takes priority in regards to overloading.
108 //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
109 // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call
110 template<typename pass_t, typename node_t>
111 static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) {
112 node = pass.previsit( node );
113 assert(node);
114 }
115
116 template<typename pass_t, typename node_t>
117 static inline auto previsit( pass_t &, const node_t *, long ) {}
118
119 // PostVisit : never mutates the passed pointer but may return a different node
120 template<typename pass_t, typename node_t>
121 static inline auto postvisit( pass_t & pass, const node_t * node, int ) -> decltype( pass.postvisit( node ), (const node_t *)nullptr ) {
122 return pass.postvisit( node );
123 }
124
125 template<typename pass_t, typename node_t>
126 static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; }
127
128 //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
129 // Deep magic (a.k.a template meta programming) continued
130 // To make the templated visitor be more expressive, we allow 'accessories' : classes/structs the implementation can inherit
131 // from in order to get extra functionallity for example
132 // class ErrorChecker : WithShortCircuiting { ... };
133 // Pass<ErrorChecker> checker;
134 // this would define a pass that uses the templated visitor with the additionnal feature that it has short circuiting
135 // Note that in all cases the accessories are not required but guarantee the requirements of the feature is matched
136 //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
137 // For several accessories, the feature is enabled by detecting that a specific field is present
138 // Use a macro the encapsulate the logic of detecting a particular field
139 // The type is not strictly enforced but does match the accessory
140 #define FIELD_PTR( name, default_type ) \
141 template< typename pass_t > \
142 static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \
143 \
144 template< typename pass_t > \
145 static inline default_type * name( pass_t &, long ) { return nullptr; }
146
147 // List of fields and their expected types
148 FIELD_PTR( env, const ast::TypeSubstitution )
149 FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > )
150 FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > )
151 FIELD_PTR( declsToAddBefore, std::list< ast::ptr< ast::Decl > > )
152 FIELD_PTR( declsToAddAfter , std::list< ast::ptr< ast::Decl > > )
153 FIELD_PTR( visit_children, __pass::bool_ref )
154 FIELD_PTR( at_cleanup, __pass::at_cleanup_t )
155 FIELD_PTR( visitor, ast::Pass<pass_t> * const )
156
157 // Remove the macro to make sure we don't clash
158 #undef FIELD_PTR
159
160 // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement.
161 // All passes which have such functions are assumed desire this behaviour
162 // detect it using the same strategy
163 namespace scope {
164 template<typename pass_t>
165 static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) {
166 pass.beginScope();
167 }
168
169 template<typename pass_t>
170 static inline void enter( pass_t &, long ) {}
171
172 template<typename pass_t>
173 static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) {
174 pass.endScope();
175 }
176
177 template<typename pass_t>
178 static inline void leave( pass_t &, long ) {}
179 };
180
181 // Finally certain pass desire an up to date indexer automatically
182 // detect the presence of a member name indexer and call all the members appropriately
183 namespace indexer {
184 // Some simple scoping rules
185 template<typename pass_t>
186 static inline auto enter( pass_t & pass, int ) -> decltype( pass.indexer.enterScope(), void() ) {
187 pass.indexer.enterScope();
188 }
189
190 template<typename pass_t>
191 static inline auto enter( pass_t &, long ) {}
192
193 template<typename pass_t>
194 static inline auto leave( pass_t & pass, int ) -> decltype( pass.indexer.leaveScope(), void() ) {
195 pass.indexer.leaveScope();
196 }
197
198 template<typename pass_t>
199 static inline auto leave( pass_t &, long ) {}
200
201 // The indexer has 2 kind of functions mostly, 1 argument and 2 arguments
202 // Create macro to condense these common patterns
203 #define INDEXER_FUNC1( func, type ) \
204 template<typename pass_t> \
205 static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.indexer.func( arg ), void() ) {\
206 pass.indexer.func( arg ); \
207 } \
208 \
209 template<typename pass_t> \
210 static inline void func( pass_t &, long, type ) {}
211
212 #define INDEXER_FUNC2( func, type1, type2 ) \
213 template<typename pass_t> \
214 static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.indexer.func( arg1, arg2 ), void () ) {\
215 pass.indexer.func( arg1, arg2 ); \
216 } \
217 \
218 template<typename pass_t> \
219 static inline void func( pass_t &, long, type1, type2 ) {}
220
221 INDEXER_FUNC1( addId , DeclarationWithType * );
222 INDEXER_FUNC1( addType , NamedTypeDecl * );
223 INDEXER_FUNC1( addStruct , StructDecl * );
224 INDEXER_FUNC1( addEnum , EnumDecl * );
225 INDEXER_FUNC1( addUnion , UnionDecl * );
226 INDEXER_FUNC1( addTrait , TraitDecl * );
227 INDEXER_FUNC2( addWith , std::list< Expression * > &, BaseSyntaxNode * );
228
229 // A few extra functions have more complicated behaviour, they are hand written
230 template<typename pass_t>
231 static inline auto addStructFwd( pass_t & pass, int, ast::StructDecl * decl ) -> decltype( pass.indexer.addStruct( decl ), void() ) {
232 ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name );
233 fwd->parameters = decl->parameters;
234 pass.indexer.addStruct( fwd );
235 }
236
237 template<typename pass_t>
238 static inline void addStructFwd( pass_t &, long, ast::StructDecl * ) {}
239
240 template<typename pass_t>
241 static inline auto addUnionFwd( pass_t & pass, int, ast::UnionDecl * decl ) -> decltype( pass.indexer.addUnion( decl ), void() ) {
242 UnionDecl * fwd = new UnionDecl( decl->name );
243 fwd->parameters = decl->parameters;
244 pass.indexer.addUnion( fwd );
245 }
246
247 template<typename pass_t>
248 static inline void addUnionFwd( pass_t &, long, ast::UnionDecl * ) {}
249
250 template<typename pass_t>
251 static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addStruct( str ), void() ) {
252 if ( ! pass.indexer.lookupStruct( str ) ) {
253 pass.indexer.addStruct( str );
254 }
255 }
256
257 template<typename pass_t>
258 static inline void addStruct( pass_t &, long, const std::string & ) {}
259
260 template<typename pass_t>
261 static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addUnion( str ), void() ) {
262 if ( ! pass.indexer.lookupUnion( str ) ) {
263 pass.indexer.addUnion( str );
264 }
265 }
266
267 template<typename pass_t>
268 static inline void addUnion( pass_t &, long, const std::string & ) {}
269
270 #undef INDEXER_FUNC1
271 #undef INDEXER_FUNC2
272 };
273 };
274};
Note: See TracBrowser for help on using the repository browser.