source: src/AST/Pass.proto.hpp@ 8fd1b7c

ADT ast-experimental
Last change on this file since 8fd1b7c was 2d0f918, checked in by Andrew Beach <ajbeach@…>, 2 years ago

Clean up methods for result1/N/Nstmt classes and the padantic pass macros.

  • Property mode set to 100644
File size: 19.1 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// Pass.impl.hpp --
8//
9// Author : Thierry Delisle
10// Created On : Thu May 09 15::37::05 2019
11// Last Modified By :
12// Last Modified On :
13// Update Count :
14//
15
16#pragma once
17// IWYU pragma: private, include "Pass.hpp"
18
19#include "Common/Iterate.hpp"
20#include "Common/Stats/Heap.h"
21namespace ast {
22 template<typename core_t> class Pass;
23 class TranslationUnit;
24 struct PureVisitor;
25 template<typename node_t> node_t * deepCopy( const node_t * );
26}
27
28#ifdef PEDANTIC_PASS_ASSERT
29#define __pedantic_pass_assert(...) assert (__VA_ARGS__)
30#define __pedantic_pass_assertf(...) assertf(__VA_ARGS__)
31#else
32#define __pedantic_pass_assert(...)
33#define __pedantic_pass_assertf(...)
34#endif
35
36namespace ast::__pass {
37
38typedef std::function<void( void * )> cleanup_func_t;
39typedef std::function<void( cleanup_func_t, void * )> at_cleanup_t;
40
41// boolean reference that may be null
42// either refers to a boolean value or is null and returns true
43class bool_ref {
44public:
45 bool_ref() = default;
46 ~bool_ref() = default;
47
48 operator bool() { return m_ref ? *m_ref : true; }
49 bool operator=( bool val ) { assert(m_ref); return *m_ref = val; }
50
51private:
52
53 friend class visit_children_guard;
54
55 bool * set( bool * val ) {
56 bool * prev = m_ref;
57 m_ref = val;
58 return prev;
59 }
60
61 bool * m_ref = nullptr;
62};
63
64// Implementation of the guard value
65// Created inside the visit scope
66class guard_value {
67public:
68 /// Push onto the cleanup
69 guard_value( at_cleanup_t * at_cleanup ) {
70 if( at_cleanup ) {
71 *at_cleanup = [this]( cleanup_func_t && func, void* val ) {
72 push( std::move( func ), val );
73 };
74 }
75 }
76
77 ~guard_value() {
78 while( !cleanups.empty() ) {
79 auto& cleanup = cleanups.top();
80 cleanup.func( cleanup.val );
81 cleanups.pop();
82 }
83 }
84
85 void push( cleanup_func_t && func, void* val ) {
86 cleanups.emplace( std::move(func), val );
87 }
88
89private:
90 struct cleanup_t {
91 cleanup_func_t func;
92 void * val;
93
94 cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {}
95 };
96
97 std::stack< cleanup_t, std::vector<cleanup_t> > cleanups;
98};
99
100// Guard structure implementation for whether or not children should be visited
101class visit_children_guard {
102public:
103
104 visit_children_guard( bool_ref * ref )
105 : m_val ( true )
106 , m_prev( ref ? ref->set( &m_val ) : nullptr )
107 , m_ref ( ref )
108 {}
109
110 ~visit_children_guard() {
111 if( m_ref ) {
112 m_ref->set( m_prev );
113 }
114 }
115
116 operator bool() { return m_val; }
117
118private:
119 bool m_val;
120 bool * m_prev;
121 bool_ref * m_ref;
122};
123
124/// "Short hand" to check if this is a valid previsit function
125/// Mostly used to make the static_assert look (and print) prettier
126template<typename core_t, typename node_t>
127struct is_valid_previsit {
128 using ret_t = decltype( std::declval<core_t*>()->previsit( std::declval<const node_t *>() ) );
129
130 static constexpr bool value = std::is_void< ret_t >::value ||
131 std::is_base_of<const node_t, typename std::remove_pointer<ret_t>::type >::value;
132};
133
134/// The result is a single node.
135template< typename node_t >
136struct result1 {
137 bool differs = false;
138 const node_t * value = nullptr;
139
140 template< typename object_t, typename super_t, typename field_t >
141 void apply( object_t * object, field_t super_t::* field ) {
142 object->*field = value;
143 }
144};
145
146/// The result is a container of statements.
147template< template<class...> class container_t >
148struct resultNstmt {
149 /// The delta/change on a single node.
150 struct delta {
151 ptr<Stmt> new_val;
152 ssize_t old_idx;
153 bool is_old;
154
155 delta(const Stmt * s, ssize_t i, bool old) :
156 new_val(s), old_idx(i), is_old(old) {}
157 };
158
159 bool differs = false;
160 container_t< delta > values;
161
162 template< typename object_t, typename super_t, typename field_t >
163 void apply( object_t * object, field_t super_t::* field ) {
164 field_t & container = object->*field;
165 __pedantic_pass_assert( container.size() <= values.size() );
166
167 auto cit = enumerate(container).begin();
168
169 container_t<ptr<Stmt>> nvals;
170 for ( delta & d : values ) {
171 if ( d.is_old ) {
172 __pedantic_pass_assert( cit.idx <= d.old_idx );
173 std::advance( cit, d.old_idx - cit.idx );
174 nvals.push_back( std::move( (*cit).val ) );
175 } else {
176 nvals.push_back( std::move( d.new_val ) );
177 }
178 }
179
180 container = std::move(nvals);
181 }
182
183 template< template<class...> class incontainer_t >
184 void take_all( incontainer_t<ptr<Stmt>> * stmts ) {
185 if ( !stmts || stmts->empty() ) return;
186
187 std::transform( stmts->begin(), stmts->end(), std::back_inserter( values ),
188 [](ast::ptr<ast::Stmt>& stmt) -> delta {
189 return delta( stmt.release(), -1, false );
190 });
191 stmts->clear();
192 differs = true;
193 }
194
195 template< template<class...> class incontainer_t >
196 void take_all( incontainer_t<ptr<Decl>> * decls ) {
197 if ( !decls || decls->empty() ) return;
198
199 std::transform( decls->begin(), decls->end(), std::back_inserter( values ),
200 [](ast::ptr<ast::Decl>& decl) -> delta {
201 ast::Decl const * d = decl.release();
202 return delta( new DeclStmt( d->location, d ), -1, false );
203 });
204 decls->clear();
205 differs = true;
206 }
207};
208
209/// The result is a container of nodes.
210template< template<class...> class container_t, typename node_t >
211struct resultN {
212 bool differs = false;
213 container_t<ptr<node_t>> values;
214
215 template< typename object_t, typename super_t, typename field_t >
216 void apply( object_t * object, field_t super_t::* field ) {
217 field_t & container = object->*field;
218 __pedantic_pass_assert( container.size() == values.size() );
219
220 for ( size_t i = 0; i < container.size(); ++i ) {
221 // Take all the elements that are different in 'values'
222 // and swap them into 'container'
223 if ( values[i] != nullptr ) swap(container[i], values[i]);
224 }
225 // Now the original containers should still have the unchanged values
226 // but also contain the new values.
227 }
228};
229
230/// Used by previsit implementation
231/// We need to reassign the result to 'node', unless the function
232/// returns void, then we just leave 'node' unchanged
233template<bool is_void>
234struct __assign;
235
236template<>
237struct __assign<true> {
238 template<typename core_t, typename node_t>
239 static inline void result( core_t & core, const node_t * & node ) {
240 core.previsit( node );
241 }
242};
243
244template<>
245struct __assign<false> {
246 template<typename core_t, typename node_t>
247 static inline void result( core_t & core, const node_t * & node ) {
248 node = core.previsit( node );
249 assertf(node, "Previsit must not return NULL");
250 }
251};
252
253/// Used by postvisit implementation
254/// We need to return the result unless the function
255/// returns void, then we just return the original node
256template<bool is_void>
257struct __return;
258
259template<>
260struct __return<true> {
261 template<typename core_t, typename node_t>
262 static inline const node_t * result( core_t & core, const node_t * & node ) {
263 core.postvisit( node );
264 return node;
265 }
266};
267
268template<>
269struct __return<false> {
270 template<typename core_t, typename node_t>
271 static inline auto result( core_t & core, const node_t * & node ) {
272 return core.postvisit( node );
273 }
274};
275
276//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
277// Deep magic (a.k.a template meta programming) to make the templated visitor work
278// Basically the goal is to make 2 previsit
279// 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of
280// 'pass.previsit( node )' that compiles will be used for that node for that type
281// This requires that this option only compile for passes that actually define an appropriate visit.
282// SFINAE will make sure the compilation errors in this function don't halt the build.
283// See http://en.cppreference.com/w/cpp/language/sfinae for details on SFINAE
284// 2 - Since the first implementation might not be specilizable, the second implementation exists and does nothing.
285// This is needed only to eliminate the need for passes to specify any kind of handlers.
286// The second implementation only works because it has a lower priority. This is due to the bogus last parameter.
287// The second implementation takes a long while the first takes an int. Since the caller always passes an literal 0
288// the first implementation takes priority in regards to overloading.
289//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
290// PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call
291template<typename core_t, typename node_t>
292static inline auto previsit( core_t & core, const node_t * & node, int ) -> decltype( core.previsit( node ), void() ) {
293 static_assert(
294 is_valid_previsit<core_t, node_t>::value,
295 "Previsit may not change the type of the node. It must return its paremeter or void."
296 );
297
298 __assign<
299 std::is_void<
300 decltype( core.previsit( node ) )
301 >::value
302 >::result( core, node );
303}
304
305template<typename core_t, typename node_t>
306static inline auto previsit( core_t &, const node_t *, long ) {}
307
308// PostVisit : never mutates the passed pointer but may return a different node
309template<typename core_t, typename node_t>
310static inline auto postvisit( core_t & core, const node_t * node, int ) ->
311 decltype( core.postvisit( node ), node->accept( *(Visitor*)nullptr ) )
312{
313 return __return<
314 std::is_void<
315 decltype( core.postvisit( node ) )
316 >::value
317 >::result( core, node );
318}
319
320template<typename core_t, typename node_t>
321static inline const node_t * postvisit( core_t &, const node_t * node, long ) { return node; }
322
323//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
324// Deep magic (a.k.a template meta programming) continued
325// To make the templated visitor be more expressive, we allow 'accessories' : classes/structs the implementation can inherit
326// from in order to get extra functionallity for example
327// class ErrorChecker : WithShortCircuiting { ... };
328// Pass<ErrorChecker> checker;
329// this would define a pass that uses the templated visitor with the additionnal feature that it has short circuiting
330// Note that in all cases the accessories are not required but guarantee the requirements of the feature is matched
331//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
332// For several accessories, the feature is enabled by detecting that a specific field is present
333// Use a macro the encapsulate the logic of detecting a particular field
334// The type is not strictly enforced but does match the accessory
335#define FIELD_PTR( name, default_type ) \
336template< typename core_t > \
337static inline auto name( core_t & core, int ) -> decltype( &core.name ) { return &core.name; } \
338\
339template< typename core_t > \
340static inline default_type * name( core_t &, long ) { return nullptr; }
341
342// List of fields and their expected types
343FIELD_PTR( typeSubs, const ast::TypeSubstitution * )
344FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > )
345FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > )
346FIELD_PTR( declsToAddBefore, std::list< ast::ptr< ast::Decl > > )
347FIELD_PTR( declsToAddAfter , std::list< ast::ptr< ast::Decl > > )
348FIELD_PTR( visit_children, __pass::bool_ref )
349FIELD_PTR( at_cleanup, __pass::at_cleanup_t )
350FIELD_PTR( visitor, ast::Pass<core_t> * const )
351
352// Remove the macro to make sure we don't clash
353#undef FIELD_PTR
354
355template< typename core_t >
356static inline auto beginTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {
357 // Stats::Heap::stacktrace_push(core_t::traceId);
358}
359
360template< typename core_t >
361static inline auto endTrace(core_t &, int) -> decltype( core_t::traceId, void() ) {
362 // Stats::Heap::stacktrace_pop();
363}
364
365template< typename core_t >
366static void beginTrace(core_t &, long) {}
367
368template< typename core_t >
369static void endTrace(core_t &, long) {}
370
371// Allows visitor to handle an error on top-level declarations, and possibly suppress the error.
372// If on_error() returns false, the error will be ignored. By default, it returns true.
373
374template< typename core_t >
375static bool on_error (core_t &, ptr<Decl> &, long) { return true; }
376
377template< typename core_t >
378static auto on_error (core_t & core, ptr<Decl> & decl, int) -> decltype(core.on_error(decl)) {
379 return core.on_error(decl);
380}
381
382template< typename core_t, typename node_t >
383static auto make_location_guard( core_t & core, node_t * node, int )
384 -> decltype( node->location, ValueGuardPtr<const CodeLocation *>( &core.location ) ) {
385 ValueGuardPtr<const CodeLocation *> guard( &core.location );
386 core.location = &node->location;
387 return guard;
388}
389
390template< typename core_t, typename node_t >
391static auto make_location_guard( core_t &, node_t *, long ) -> int {
392 return 0;
393}
394
395// Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement.
396// All passes which have such functions are assumed desire this behaviour
397// detect it using the same strategy
398namespace scope {
399 template<typename core_t>
400 static inline auto enter( core_t & core, int ) -> decltype( core.beginScope(), void() ) {
401 core.beginScope();
402 }
403
404 template<typename core_t>
405 static inline void enter( core_t &, long ) {}
406
407 template<typename core_t>
408 static inline auto leave( core_t & core, int ) -> decltype( core.endScope(), void() ) {
409 core.endScope();
410 }
411
412 template<typename core_t>
413 static inline void leave( core_t &, long ) {}
414} // namespace scope
415
416// Certain passes desire an up to date symbol table automatically
417// detect the presence of a member name `symtab` and call all the members appropriately
418namespace symtab {
419 // Some simple scoping rules
420 template<typename core_t>
421 static inline auto enter( core_t & core, int ) -> decltype( core.symtab, void() ) {
422 core.symtab.enterScope();
423 }
424
425 template<typename core_t>
426 static inline auto enter( core_t &, long ) {}
427
428 template<typename core_t>
429 static inline auto leave( core_t & core, int ) -> decltype( core.symtab, void() ) {
430 core.symtab.leaveScope();
431 }
432
433 template<typename core_t>
434 static inline auto leave( core_t &, long ) {}
435
436 // The symbol table has 2 kind of functions mostly, 1 argument and 2 arguments
437 // Create macro to condense these common patterns
438 #define SYMTAB_FUNC1( func, type ) \
439 template<typename core_t> \
440 static inline auto func( core_t & core, int, type arg ) -> decltype( core.symtab.func( arg ), void() ) {\
441 core.symtab.func( arg ); \
442 } \
443 \
444 template<typename core_t> \
445 static inline void func( core_t &, long, type ) {}
446
447 #define SYMTAB_FUNC2( func, type1, type2 ) \
448 template<typename core_t> \
449 static inline auto func( core_t & core, int, type1 arg1, type2 arg2 ) -> decltype( core.symtab.func( arg1, arg2 ), void () ) {\
450 core.symtab.func( arg1, arg2 ); \
451 } \
452 \
453 template<typename core_t> \
454 static inline void func( core_t &, long, type1, type2 ) {}
455
456 SYMTAB_FUNC1( addId , const DeclWithType * );
457 SYMTAB_FUNC1( addType , const NamedTypeDecl * );
458 SYMTAB_FUNC1( addStruct , const StructDecl * );
459 SYMTAB_FUNC1( addEnum , const EnumDecl * );
460 SYMTAB_FUNC1( addUnion , const UnionDecl * );
461 SYMTAB_FUNC1( addTrait , const TraitDecl * );
462 SYMTAB_FUNC2( addWith , const std::vector< ptr<Expr> > &, const Decl * );
463
464 // A few extra functions have more complicated behaviour, they are hand written
465 template<typename core_t>
466 static inline auto addStructFwd( core_t & core, int, const ast::StructDecl * decl ) -> decltype( core.symtab.addStruct( decl ), void() ) {
467 ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name );
468 for ( const auto & param : decl->params ) {
469 fwd->params.push_back( deepCopy( param.get() ) );
470 }
471 core.symtab.addStruct( fwd );
472 }
473
474 template<typename core_t>
475 static inline void addStructFwd( core_t &, long, const ast::StructDecl * ) {}
476
477 template<typename core_t>
478 static inline auto addUnionFwd( core_t & core, int, const ast::UnionDecl * decl ) -> decltype( core.symtab.addUnion( decl ), void() ) {
479 ast::UnionDecl * fwd = new ast::UnionDecl( decl->location, decl->name );
480 for ( const auto & param : decl->params ) {
481 fwd->params.push_back( deepCopy( param.get() ) );
482 }
483 core.symtab.addUnion( fwd );
484 }
485
486 template<typename core_t>
487 static inline void addUnionFwd( core_t &, long, const ast::UnionDecl * ) {}
488
489 template<typename core_t>
490 static inline auto addStruct( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addStruct( str ), void() ) {
491 if ( ! core.symtab.lookupStruct( str ) ) {
492 core.symtab.addStruct( str );
493 }
494 }
495
496 template<typename core_t>
497 static inline void addStruct( core_t &, long, const std::string & ) {}
498
499 template<typename core_t>
500 static inline auto addUnion( core_t & core, int, const std::string & str ) -> decltype( core.symtab.addUnion( str ), void() ) {
501 if ( ! core.symtab.lookupUnion( str ) ) {
502 core.symtab.addUnion( str );
503 }
504 }
505
506 template<typename core_t>
507 static inline void addUnion( core_t &, long, const std::string & ) {}
508
509 #undef SYMTAB_FUNC1
510 #undef SYMTAB_FUNC2
511} // namespace symtab
512
513// Some passes need to mutate TypeDecl and properly update their pointing TypeInstType.
514// Detect the presence of a member name `subs` and call all members appropriately
515namespace forall {
516 // Some simple scoping rules
517 template<typename core_t>
518 static inline auto enter( core_t & core, int, const ast::FunctionType * type )
519 -> decltype( core.subs, void() ) {
520 if ( ! type->forall.empty() ) core.subs.beginScope();
521 }
522
523 template<typename core_t>
524 static inline auto enter( core_t &, long, const ast::FunctionType * ) {}
525
526 template<typename core_t>
527 static inline auto leave( core_t & core, int, const ast::FunctionType * type )
528 -> decltype( core.subs, void() ) {
529 if ( ! type->forall.empty() ) { core.subs.endScope(); }
530 }
531
532 template<typename core_t>
533 static inline auto leave( core_t &, long, const ast::FunctionType * ) {}
534
535 // Replaces a TypeInstType's base TypeDecl according to the table
536 template<typename core_t>
537 static inline auto replace( core_t & core, int, const ast::TypeInstType *& inst )
538 -> decltype( core.subs, void() ) {
539 inst = ast::mutate_field(
540 inst, &ast::TypeInstType::base, core.subs.replace( inst->base ) );
541 }
542
543 template<typename core_t>
544 static inline auto replace( core_t &, long, const ast::TypeInstType *& ) {}
545} // namespace forall
546
547// For passes that need access to the global context. Searches `translationUnit`
548namespace translation_unit {
549 template<typename core_t>
550 static inline auto get_cptr( core_t & core, int )
551 -> decltype( &core.translationUnit ) {
552 return &core.translationUnit;
553 }
554
555 template<typename core_t>
556 static inline const TranslationUnit ** get_cptr( core_t &, long ) {
557 return nullptr;
558 }
559}
560
561// For passes, usually utility passes, that have a result.
562namespace result {
563 template<typename core_t>
564 static inline auto get( core_t & core, char ) -> decltype( core.result() ) {
565 return core.result();
566 }
567
568 template<typename core_t>
569 static inline auto get( core_t & core, int ) -> decltype( core.result ) {
570 return core.result;
571 }
572
573 template<typename core_t>
574 static inline void get( core_t &, long ) {}
575}
576
577} // namespace ast::__pass
578
579#undef __pedantic_pass_assertf
580#undef __pedantic_pass_assert
Note: See TracBrowser for help on using the repository browser.