source: src/AST/Pass.hpp@ 994030ce

Last change on this file since 994030ce was b9fe89b, checked in by Michael Brooks <mlbrooks@…>, 2 years 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.