source: src/AST/Pass.hpp @ 27f2bef

Last change on this file since 27f2bef was b9fe89b, checked in by Michael Brooks <mlbrooks@…>, 13 months ago

Make the symbol table's error-checking times explicit.

Previously, error checking happened on all WithSymbolTable? uses. Error checking means having a symbol-table add operation potentially cause a user-visible error report. Now, this only happens on the resolver pass's symbol table, while other passes' run in an "assert no errors can happen" mode.

An "ignore errors for now" mode is implemented too, which will be used in upcoming commits, for pre-resolver passes that use the symbol table.

  • Property mode set to 100644
File size: 21.6 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.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 "AST/Pass.hpp"
18
19#include <functional>
20#include <list>
21#include <stack>
22
23#include "AST/Fwd.hpp"
24#include "AST/Node.hpp"
25
26#include "AST/Attribute.hpp"
27#include "AST/Decl.hpp"
28#include "AST/Expr.hpp"
29#include "AST/Init.hpp"
30#include "AST/Stmt.hpp"
31#include "AST/Type.hpp"
32
33#include "AST/Visitor.hpp"
34
35#include "AST/SymbolTable.hpp"
36
37// Private prelude header, needed for some of the magic tricks this class pulls off
38#include "AST/Pass.proto.hpp"
39
40namespace ast {
41//-------------------------------------------------------------------------------------------------
42// Templated visitor type
43// To use declare a Pass< YOUR VISITOR TYPE >
44// The visitor type should specify the previsit/postvisit for types that are desired.
45// Note: previsit/postvisit must be **public** members
46//
47// Several additional features are available through inheritance
48// | PureVisitor           - makes the visitor pure, it never modifies nodes in place and always
49//                           clones nodes it needs to make changes to
50// | WithConstTypeSubstitution - provides polymorphic const TypeSubstitution * typeSubs for the
51//                           current expression
52// | WithStmtsToAdd        - provides the ability to insert statements before or after the current
53//                           statement by adding new statements into stmtsToAddBefore or
54//                           stmtsToAddAfter respectively.
55// | WithDeclsToAdd        - provides the ability to insert declarations before or after the
56//                           current declarations by adding new DeclStmt into declsToAddBefore or
57//                           declsToAddAfter respectively.
58// | WithShortCircuiting   - provides the ability to skip visiting child nodes; set visit_children
59//                           to false in pre{visit,visit} to skip visiting children
60// | WithGuards            - provides the ability to save/restore data like a LIFO stack; to save,
61//                           call GuardValue with the variable to save, the variable will
62//                           automatically be restored to its previous value after the
63//                           corresponding postvisit/postmutate teminates.
64// | WithVisitorRef        - provides an pointer to the templated visitor wrapper
65// | WithSymbolTable       - provides symbol table functionality
66//
67// Other Special Members:
68// | beginScope            - A method with no parameters or return value, called each time the
69//                           visitor enters a block.
70// | endScope              - A method with no parameters or return value, called each time the
71//                           visitor leaves a block.
72// | result                - Either a method that takes no parameters or a field. If a method (or
73//                           callable field) get_result calls it, otherwise the value is returned.
74//-------------------------------------------------------------------------------------------------
75template< typename core_t >
76class Pass final : public ast::Visitor {
77public:
78        using core_type = core_t;
79        using type = Pass<core_t>;
80
81        /// Forward any arguments to the pass constructor
82        /// Propagate 'this' if necessary
83        template< typename... Args >
84        Pass( Args &&... args)
85                : core( std::forward<Args>( args )... )
86        {
87                // After the pass is constructed, check if it wants the have a pointer to the wrapping visitor
88                type * const * visitor = __pass::visitor( core, 0 );
89                if ( visitor ) {
90                        *const_cast<type **>( visitor ) = this;
91                }
92        }
93
94        virtual ~Pass() = default;
95
96        /// Storage for the actual pass.
97        core_t core;
98
99        /// If the core defines a result, call it if possible, otherwise return it.
100        inline auto get_result() -> decltype( __pass::result::get( core, '0' ) ) {
101                return __pass::result::get( core, '0' );
102        }
103
104        /// Construct and run a pass on a translation unit.
105        template< typename... Args >
106        static void run( TranslationUnit & decls, Args &&... args ) {
107                Pass<core_t> visitor( std::forward<Args>( args )... );
108                accept_all( decls, visitor );
109        }
110
111        /// Contruct and run a pass on a pointer to extract a value.
112        template< typename node_type, typename... Args >
113        static auto read( node_type const * node, Args&&... args ) {
114                Pass<core_t> visitor( std::forward<Args>( args )... );
115                auto const * temp = node->accept( visitor );
116                assert( temp == node );
117                return visitor.get_result();
118        }
119
120        // Versions of the above for older compilers.
121        template< typename... Args >
122        static void run( TranslationUnit & decls ) {
123                Pass<core_t> visitor;
124                accept_all( decls, visitor );
125        }
126
127        template< typename node_type, typename... Args >
128        static auto read( node_type const * node ) {
129                Pass<core_t> visitor;
130                auto const * temp = node->accept( visitor );
131                assert( temp == node );
132                return visitor.get_result();
133        }
134
135        /// Visit function declarations
136        const ast::DeclWithType *     visit( const ast::ObjectDecl           * ) override final;
137        const ast::DeclWithType *     visit( const ast::FunctionDecl         * ) override final;
138        const ast::Decl *             visit( const ast::StructDecl           * ) override final;
139        const ast::Decl *             visit( const ast::UnionDecl            * ) override final;
140        const ast::Decl *             visit( const ast::EnumDecl             * ) override final;
141        const ast::Decl *             visit( const ast::TraitDecl            * ) override final;
142        const ast::Decl *             visit( const ast::TypeDecl             * ) override final;
143        const ast::Decl *             visit( const ast::TypedefDecl          * ) override final;
144        const ast::AsmDecl *          visit( const ast::AsmDecl              * ) override final;
145        const ast::DirectiveDecl *    visit( const ast::DirectiveDecl        * ) override final;
146        const ast::StaticAssertDecl * visit( const ast::StaticAssertDecl     * ) override final;
147        const ast::DeclWithType *     visit( const ast::InlineMemberDecl     * ) override final;
148        const ast::CompoundStmt *     visit( const ast::CompoundStmt         * ) override final;
149        const ast::Stmt *             visit( const ast::ExprStmt             * ) override final;
150        const ast::Stmt *             visit( const ast::AsmStmt              * ) override final;
151        const ast::Stmt *             visit( const ast::DirectiveStmt        * ) override final;
152        const ast::Stmt *             visit( const ast::IfStmt               * ) override final;
153        const ast::Stmt *             visit( const ast::WhileDoStmt          * ) override final;
154        const ast::Stmt *             visit( const ast::ForStmt              * ) override final;
155        const ast::Stmt *             visit( const ast::SwitchStmt           * ) override final;
156        const ast::CaseClause *       visit( const ast::CaseClause           * ) override final;
157        const ast::Stmt *             visit( const ast::BranchStmt           * ) override final;
158        const ast::Stmt *             visit( const ast::ReturnStmt           * ) override final;
159        const ast::Stmt *             visit( const ast::ThrowStmt            * ) override final;
160        const ast::Stmt *             visit( const ast::TryStmt              * ) override final;
161        const ast::CatchClause *      visit( const ast::CatchClause          * ) override final;
162        const ast::FinallyClause *    visit( const ast::FinallyClause        * ) override final;
163        const ast::Stmt *             visit( const ast::SuspendStmt          * ) override final;
164    const ast::WhenClause *       visit( const ast::WhenClause           * ) override final;
165        const ast::Stmt *             visit( const ast::WaitForStmt          * ) override final;
166        const ast::WaitForClause *    visit( const ast::WaitForClause        * ) override final;
167    const ast::Stmt *             visit( const ast::WaitUntilStmt        * ) override final;
168        const ast::Decl *             visit( const ast::WithStmt             * ) override final;
169        const ast::NullStmt *         visit( const ast::NullStmt             * ) override final;
170        const ast::Stmt *             visit( const ast::DeclStmt             * ) override final;
171        const ast::Stmt *             visit( const ast::ImplicitCtorDtorStmt * ) override final;
172        const ast::Stmt *             visit( const ast::MutexStmt            * ) override final;
173        const ast::Expr *             visit( const ast::ApplicationExpr      * ) override final;
174        const ast::Expr *             visit( const ast::UntypedExpr          * ) override final;
175        const ast::Expr *             visit( const ast::NameExpr             * ) override final;
176        const ast::Expr *             visit( const ast::QualifiedNameExpr        * ) override final;
177        const ast::Expr *             visit( const ast::AddressExpr          * ) override final;
178        const ast::Expr *             visit( const ast::LabelAddressExpr     * ) override final;
179        const ast::Expr *             visit( const ast::CastExpr             * ) override final;
180        const ast::Expr *             visit( const ast::KeywordCastExpr      * ) override final;
181        const ast::Expr *             visit( const ast::VirtualCastExpr      * ) override final;
182        const ast::Expr *             visit( const ast::UntypedMemberExpr    * ) override final;
183        const ast::Expr *             visit( const ast::MemberExpr           * ) override final;
184        const ast::Expr *             visit( const ast::VariableExpr         * ) override final;
185        const ast::Expr *             visit( const ast::ConstantExpr         * ) override final;
186        const ast::Expr *             visit( const ast::SizeofExpr           * ) override final;
187        const ast::Expr *             visit( const ast::AlignofExpr          * ) override final;
188        const ast::Expr *             visit( const ast::UntypedOffsetofExpr  * ) override final;
189        const ast::Expr *             visit( const ast::OffsetofExpr         * ) override final;
190        const ast::Expr *             visit( const ast::OffsetPackExpr       * ) override final;
191        const ast::Expr *             visit( const ast::LogicalExpr          * ) override final;
192        const ast::Expr *             visit( const ast::ConditionalExpr      * ) override final;
193        const ast::Expr *             visit( const ast::CommaExpr            * ) override final;
194        const ast::Expr *             visit( const ast::TypeExpr             * ) override final;
195        const ast::Expr *             visit( const ast::DimensionExpr        * ) override final;
196        const ast::Expr *             visit( const ast::AsmExpr              * ) override final;
197        const ast::Expr *             visit( const ast::ImplicitCopyCtorExpr * ) override final;
198        const ast::Expr *             visit( const ast::ConstructorExpr      * ) override final;
199        const ast::Expr *             visit( const ast::CompoundLiteralExpr  * ) override final;
200        const ast::Expr *             visit( const ast::RangeExpr            * ) override final;
201        const ast::Expr *             visit( const ast::UntypedTupleExpr     * ) override final;
202        const ast::Expr *             visit( const ast::TupleExpr            * ) override final;
203        const ast::Expr *             visit( const ast::TupleIndexExpr       * ) override final;
204        const ast::Expr *             visit( const ast::TupleAssignExpr      * ) override final;
205        const ast::Expr *             visit( const ast::StmtExpr             * ) override final;
206        const ast::Expr *             visit( const ast::UniqueExpr           * ) override final;
207        const ast::Expr *             visit( const ast::UntypedInitExpr      * ) override final;
208        const ast::Expr *             visit( const ast::InitExpr             * ) override final;
209        const ast::Expr *             visit( const ast::DeletedExpr          * ) override final;
210        const ast::Expr *             visit( const ast::DefaultArgExpr       * ) override final;
211        const ast::Expr *             visit( const ast::GenericExpr          * ) override final;
212        const ast::Type *             visit( const ast::VoidType             * ) override final;
213        const ast::Type *             visit( const ast::BasicType            * ) override final;
214        const ast::Type *             visit( const ast::PointerType          * ) override final;
215        const ast::Type *             visit( const ast::ArrayType            * ) override final;
216        const ast::Type *             visit( const ast::ReferenceType        * ) override final;
217        const ast::Type *             visit( const ast::QualifiedType        * ) override final;
218        const ast::Type *             visit( const ast::FunctionType         * ) override final;
219        const ast::Type *             visit( const ast::StructInstType       * ) override final;
220        const ast::Type *             visit( const ast::UnionInstType        * ) override final;
221        const ast::Type *             visit( const ast::EnumInstType         * ) override final;
222        const ast::Type *             visit( const ast::TraitInstType        * ) override final;
223        const ast::Type *             visit( const ast::TypeInstType         * ) override final;
224        const ast::Type *             visit( const ast::TupleType            * ) override final;
225        const ast::Type *             visit( const ast::TypeofType           * ) override final;
226        const ast::Type *             visit( const ast::VTableType           * ) override final;
227        const ast::Type *             visit( const ast::VarArgsType          * ) override final;
228        const ast::Type *             visit( const ast::ZeroType             * ) override final;
229        const ast::Type *             visit( const ast::OneType              * ) override final;
230        const ast::Type *             visit( const ast::GlobalScopeType      * ) override final;
231        const ast::Designation *      visit( const ast::Designation          * ) override final;
232        const ast::Init *             visit( const ast::SingleInit           * ) override final;
233        const ast::Init *             visit( const ast::ListInit             * ) override final;
234        const ast::Init *             visit( const ast::ConstructorInit      * ) override final;
235        const ast::Attribute *        visit( const ast::Attribute            * ) override final;
236        const ast::TypeSubstitution * visit( const ast::TypeSubstitution     * ) override final;
237
238        template<typename core_type>
239        friend void accept_all( std::list< ptr<Decl> > & decls, Pass<core_type>& visitor );
240
241        bool isInFunction() const {
242                return inFunction;
243        }
244
245private:
246
247        bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children(core, 0); return ptr ? *ptr : true; }
248
249private:
250
251        __pass::result1<ast::Stmt> call_accept( const ast::Stmt * );
252        __pass::result1<ast::Expr> call_accept( const ast::Expr * );
253
254        /// This has a `type` member that is the return type for the
255        /// generic call_accept if the generic call_accept is defined.
256        template< typename node_t >
257        using generic_call_accept_result =
258                std::enable_if<
259                                !std::is_base_of<ast::Expr, node_t>::value &&
260                                !std::is_base_of<ast::Stmt, node_t>::value
261                        , __pass::result1<
262                                typename std::remove_pointer< typename std::result_of<
263                                        decltype(&node_t::accept)(node_t*, type&) >::type >::type
264                        >
265                >;
266
267        template< typename node_t >
268        auto call_accept( const node_t * node )
269                -> typename generic_call_accept_result<node_t>::type;
270
271        // requests WithStmtsToAdd directly add to this statement, as if it is a compound.
272        __pass::result1<ast::Stmt> call_accept_as_compound(const ast::Stmt *);
273
274        // requests type environment to be updated (why is it implemented like this?)
275        __pass::result1<ast::Expr> call_accept_top(const ast::Expr *);
276
277        template< template <class...> class container_t >
278        __pass::resultNstmt<container_t> call_accept( const container_t< ptr<Stmt> > & );
279
280        template< template <class...> class container_t, typename node_t >
281        __pass::resultN< container_t, node_t > call_accept( const container_t< ptr<node_t> > & container );
282
283public:
284        /// Logic to call the accept and mutate the parent if needed, delegates call to accept
285        template<typename node_t, typename parent_t, typename field_t>
286        void maybe_accept(const node_t * &, field_t parent_t::* field);
287
288        template<typename node_t, typename parent_t, typename field_t>
289        void maybe_accept_as_compound(const node_t * &, field_t parent_t::* field);
290
291        template<typename node_t, typename parent_t, typename field_t>
292        void maybe_accept_top(const node_t * &, field_t parent_t::* field);
293
294private:
295        /// Internal RAII guard for symbol table features
296        struct guard_symtab {
297                guard_symtab( Pass<core_t> & pass ): pass( pass ) { __pass::symtab::enter(pass.core, 0); }
298                ~guard_symtab()                                   { __pass::symtab::leave(pass.core, 0); }
299                Pass<core_t> & pass;
300        };
301
302        /// Internal RAII guard for scope features
303        struct guard_scope {
304                guard_scope( Pass<core_t> & pass ): pass( pass ) { __pass::scope::enter(pass.core, 0); }
305                ~guard_scope()                                   { __pass::scope::leave(pass.core, 0); }
306                Pass<core_t> & pass;
307        };
308
309        /// Internal RAII guard for forall substitutions
310        struct guard_forall_subs {
311                guard_forall_subs( Pass<core_t> & pass, const FunctionType * type )
312                : pass( pass ), type( type ) { __pass::forall::enter(pass.core, 0, type ); }
313                ~guard_forall_subs()         { __pass::forall::leave(pass.core, 0, type ); }
314                Pass<core_t> & pass;
315                const FunctionType * type;
316        };
317
318private:
319        bool inFunction = false;
320        bool atFunctionTop = false;
321};
322
323/// Apply a pass to an entire translation unit
324template<typename core_t>
325void accept_all( std::list< ast::ptr<ast::Decl> > &, ast::Pass<core_t> & visitor );
326
327template<typename core_t>
328void accept_all( ast::TranslationUnit &, ast::Pass<core_t> & visitor );
329
330//-------------------------------------------------------------------------------------------------
331// PASS ACCESSORIES
332//-------------------------------------------------------------------------------------------------
333
334/// If used the visitor will always clone nodes.
335struct PureVisitor {};
336
337/// Keep track of the nearest parent node's location field.
338struct WithCodeLocation {
339        const CodeLocation * location = nullptr;
340};
341
342/// Keep track of the polymorphic const TypeSubstitution * typeSubs for the current expression.
343struct WithConstTypeSubstitution {
344        const TypeSubstitution * typeSubs = nullptr;
345};
346
347/// Used if visitor requires added statements before or after the current node.
348/// The Pass template handles what *before* and *after* means automatically
349template< template<class...> class container_t = std::list >
350struct WithStmtsToAdd {
351        container_t< ptr<Stmt> > stmtsToAddBefore;
352        container_t< ptr<Stmt> > stmtsToAddAfter;
353};
354
355/// Used if visitor requires added declarations before or after the current node.
356/// The Pass template handles what *before* and *after* means automatically
357template< template<class...> class container_t = std::list >
358struct WithDeclsToAdd {
359        container_t< ptr<Decl> > declsToAddBefore;
360        container_t< ptr<Decl> > declsToAddAfter;
361};
362
363/// Use if visitation should stop at certain levels
364/// set visit_children false of all child nodes should be ignored
365struct WithShortCircuiting {
366        __pass::bool_ref visit_children;
367};
368
369/// Used to restore values/functions/etc. when the Pass finishes visiting this node
370class WithGuards {
371        __pass::at_cleanup_t at_cleanup = [](__pass::cleanup_func_t, void*) {
372                std::cerr << "No cleanup function was set" << std::endl;
373                abort();
374        };
375
376        template< typename core_t>
377        friend auto __pass::at_cleanup( core_t & core, int ) -> decltype( &core.at_cleanup );
378public:
379
380        /// When this node is finished being visited, restore the value of a variable
381        /// You may assign to the return value to set the new value in the same statement.
382        template< typename T >
383        T& GuardValue( T& val ) {
384                at_cleanup( [ val ]( void * newVal ) {
385                        * static_cast< T * >( newVal ) = val;
386                }, static_cast< void * >( & val ) );
387                return val;
388        }
389
390        /// On the object, all beginScope now and endScope when the current node is finished being visited
391        template< typename T >
392        void GuardScope( T& val ) {
393                val.beginScope();
394                at_cleanup( []( void * val ) {
395                        static_cast< T * >( val )->endScope();
396                }, static_cast< void * >( & val ) );
397        }
398
399        /// When this node is finished being visited, call a function
400        template< typename Func >
401        void GuardAction( Func func ) {
402                at_cleanup( [func](void *) { func(); }, nullptr );
403        }
404};
405
406/// Used to get a pointer to the pass with its wrapped type
407template<typename core_t>
408struct WithVisitorRef {
409        Pass<core_t> * const visitor = nullptr;
410
411        bool isInFunction() const {
412                return visitor->isInFunction();
413        }
414};
415
416/// Use when the templated visitor should update the symbol table,
417/// that is, when your pass core needs to query the symbol table.
418/// Expected setups:
419/// - For master passes that kick off at the compilation unit
420///   - before resolver: extend WithSymbolTableX<IgnoreErrors>
421///   - after resolver: extend WithSymbolTable and use defaults
422///   - (FYI, for completeness, the resolver's main pass uses ValidateOnAdd when it kicks off)
423/// - For helper passes that kick off at arbitrary points in the AST:
424///   - take an existing symbol table as a parameter, extend WithSymbolTable,
425///     and construct with WithSymbolTable(const SymbolTable &)
426struct WithSymbolTable {
427        WithSymbolTable(const ast::SymbolTable & from) : symtab(from) {}
428        WithSymbolTable(ast::SymbolTable::ErrorDetection errorMode = ast::SymbolTable::ErrorDetection::AssertClean) : symtab(errorMode) {}
429        ast::SymbolTable symtab;
430};
431template <ast::SymbolTable::ErrorDetection errorMode>
432struct WithSymbolTableX : WithSymbolTable {
433        WithSymbolTableX() : WithSymbolTable(errorMode) {}
434};
435
436/// Used to get a pointer to the wrapping TranslationUnit.
437struct WithConstTranslationUnit {
438        const TranslationUnit * translationUnit = nullptr;
439
440        const TranslationUnit & transUnit() const {
441                assertf( translationUnit, "WithConstTranslationUnit not set-up." );
442                return *translationUnit;
443        }
444};
445
446}
447
448#include "Common/Stats.h"
449
450namespace ast {
451extern struct PassVisitorStats {
452        size_t depth = 0;
453        Stats::Counters::MaxCounter<double> * max;
454        Stats::Counters::AverageCounter<double> * avg;
455} pass_visitor_stats;
456}
457
458#include "AST/Pass.impl.hpp"
Note: See TracBrowser for help on using the repository browser.