// // Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo // // The contents of this file are covered under the licence agreement in the // file "LICENCE" distributed with Cforall. // // Pass.impl.hpp -- // // Author : Thierry Delisle // Created On : Thu May 09 15::37::05 2019 // Last Modified By : // Last Modified On : // Update Count : // #pragma once // IWYU pragma: private, include "Pass.hpp" namespace ast { template class Pass; namespace __pass { typedef std::function cleanup_func_t; typedef std::function at_cleanup_t; // boolean reference that may be null // either refers to a boolean value or is null and returns true class bool_ref { public: bool_ref() = default; ~bool_ref() = default; operator bool() { return m_ref ? *m_ref : true; } bool operator=( bool val ) { assert(m_ref); return *m_ref = val; } private: friend class visit_children_guard; bool * set( bool * val ) { bool * prev = m_ref; m_ref = val; return prev; } bool * m_ref = nullptr; }; // Implementation of the guard value // Created inside the visit scope class guard_value { public: /// Push onto the cleanup guard_value( at_cleanup_t * at_cleanup ) { if( at_cleanup ) { *at_cleanup = [this]( cleanup_func_t && func, void* val ) { push( std::move( func ), val ); }; } } ~guard_value() { while( !cleanups.empty() ) { auto& cleanup = cleanups.top(); cleanup.func( cleanup.val ); cleanups.pop(); } } void push( cleanup_func_t && func, void* val ) { cleanups.emplace( std::move(func), val ); } private: struct cleanup_t { cleanup_func_t func; void * val; cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {} }; std::stack< cleanup_t > cleanups; }; // Guard structure implementation for whether or not children should be visited class visit_children_guard { public: visit_children_guard( bool_ref * ref ) : m_val ( true ) , m_prev( ref ? ref->set( &m_val ) : nullptr ) , m_ref ( ref ) {} ~visit_children_guard() { if( m_ref ) { m_ref->set( m_prev ); } } operator bool() { return m_val; } private: bool m_val; bool * m_prev; bool_ref * m_ref; }; //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Deep magic (a.k.a template meta programming) to make the templated visitor work // Basically the goal is to make 2 previsit // 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of // 'pass.previsit( node )' that compiles will be used for that node for that type // This requires that this option only compile for passes that actually define an appropriate visit. // SFINAE will make sure the compilation errors in this function don't halt the build. // See http://en.cppreference.com/w/cpp/language/sfinae for details on SFINAE // 2 - Since the first implementation might not be specilizable, the second implementation exists and does nothing. // This is needed only to eliminate the need for passes to specify any kind of handlers. // The second implementation only works because it has a lower priority. This is due to the bogus last parameter. // The second implementation takes a long while the first takes an int. Since the caller always passes an literal 0 // the first implementation takes priority in regards to overloading. //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call template static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) { node = pass.previsit( node ); assert(node); } template static inline auto previsit( pass_t &, const node_t *, long ) {} // PostVisit : never mutates the passed pointer but may return a different node template static inline auto postvisit( pass_t & pass, const node_t * node, int ) -> decltype( pass.postvisit( node ), (const node_t *)nullptr ) { return pass.postvisit( node ); } template static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; } //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Deep magic (a.k.a template meta programming) continued // To make the templated visitor be more expressive, we allow 'accessories' : classes/structs the implementation can inherit // from in order to get extra functionallity for example // class ErrorChecker : WithShortCircuiting { ... }; // Pass checker; // this would define a pass that uses the templated visitor with the additionnal feature that it has short circuiting // Note that in all cases the accessories are not required but guarantee the requirements of the feature is matched //------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // For several accessories, the feature is enabled by detecting that a specific field is present // Use a macro the encapsulate the logic of detecting a particular field // The type is not strictly enforced but does match the accessory #define FIELD_PTR( name, default_type ) \ template< typename pass_t > \ static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \ \ template< typename pass_t > \ static inline default_type * name( pass_t &, long ) { return nullptr; } // List of fields and their expected types FIELD_PTR( env, const ast::TypeSubstitution ) FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > ) FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > ) FIELD_PTR( declsToAddBefore, std::list< ast::ptr< ast::Decl > > ) FIELD_PTR( declsToAddAfter , std::list< ast::ptr< ast::Decl > > ) FIELD_PTR( visit_children, __pass::bool_ref ) FIELD_PTR( at_cleanup, __pass::at_cleanup_t ) FIELD_PTR( visitor, ast::Pass * const ) // Remove the macro to make sure we don't clash #undef FIELD_PTR // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement. // All passes which have such functions are assumed desire this behaviour // detect it using the same strategy namespace scope { template static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) { pass.beginScope(); } template static inline void enter( pass_t &, long ) {} template static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) { pass.endScope(); } template static inline void leave( pass_t &, long ) {} }; // Finally certain pass desire an up to date indexer automatically // detect the presence of a member name indexer and call all the members appropriately namespace indexer { // Some simple scoping rules template static inline auto enter( pass_t & pass, int ) -> decltype( pass.indexer.enterScope(), void() ) { pass.indexer.enterScope(); } template static inline auto enter( pass_t &, long ) {} template static inline auto leave( pass_t & pass, int ) -> decltype( pass.indexer.leaveScope(), void() ) { pass.indexer.leaveScope(); } template static inline auto leave( pass_t &, long ) {} // The indexer has 2 kind of functions mostly, 1 argument and 2 arguments // Create macro to condense these common patterns #define INDEXER_FUNC1( func, type ) \ template \ static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.indexer.func( arg ), void() ) {\ pass.indexer.func( arg ); \ } \ \ template \ static inline void func( pass_t &, long, type ) {} #define INDEXER_FUNC2( func, type1, type2 ) \ template \ static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.indexer.func( arg1, arg2 ), void () ) {\ pass.indexer.func( arg1, arg2 ); \ } \ \ template \ static inline void func( pass_t &, long, type1, type2 ) {} INDEXER_FUNC1( addId , DeclWithType * ); INDEXER_FUNC1( addType , NamedTypeDecl * ); INDEXER_FUNC1( addStruct , StructDecl * ); INDEXER_FUNC1( addEnum , EnumDecl * ); INDEXER_FUNC1( addUnion , UnionDecl * ); INDEXER_FUNC1( addTrait , TraitDecl * ); INDEXER_FUNC2( addWith , std::list< Expression * > &, Node * ); // A few extra functions have more complicated behaviour, they are hand written // template // static inline auto addStructFwd( pass_t & pass, int, ast::StructDecl * decl ) -> decltype( pass.indexer.addStruct( decl ), void() ) { // ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name ); // fwd->parameters = decl->parameters; // pass.indexer.addStruct( fwd ); // } // template // static inline void addStructFwd( pass_t &, long, ast::StructDecl * ) {} // template // static inline auto addUnionFwd( pass_t & pass, int, ast::UnionDecl * decl ) -> decltype( pass.indexer.addUnion( decl ), void() ) { // UnionDecl * fwd = new UnionDecl( decl->name ); // fwd->parameters = decl->parameters; // pass.indexer.addUnion( fwd ); // } // template // static inline void addUnionFwd( pass_t &, long, ast::UnionDecl * ) {} // template // static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addStruct( str ), void() ) { // if ( ! pass.indexer.lookupStruct( str ) ) { // pass.indexer.addStruct( str ); // } // } // template // static inline void addStruct( pass_t &, long, const std::string & ) {} // template // static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addUnion( str ), void() ) { // if ( ! pass.indexer.lookupUnion( str ) ) { // pass.indexer.addUnion( str ); // } // } // template // static inline void addUnion( pass_t &, long, const std::string & ) {} #undef INDEXER_FUNC1 #undef INDEXER_FUNC2 }; }; };