source: src/InitTweak/FixInitNew.cpp@ 5dbb9f3

Last change on this file since 5dbb9f3 was fb4dc28, checked in by Andrew Beach <ajbeach@…>, 2 years ago

Moved new ast code out of one of the old files. The new file may have to change if SymTab is removed entirely, but for now at least, there is a lot less template code in headers.

  • Property mode set to 100644
File size: 58.8 KB
Line 
1#include "FixInit.h"
2
3#include <stddef.h> // for NULL
4#include <algorithm> // for set_difference, copy_if
5#include <cassert> // for assert, strict_dynamic_cast
6#include <iostream> // for operator<<, ostream, basic_ost...
7#include <iterator> // for insert_iterator, back_inserter
8#include <list> // for _List_iterator, list, list<>::...
9#include <map> // for _Rb_tree_iterator, _Rb_tree_co...
10#include <memory> // for allocator_traits<>::value_type
11#include <set> // for set, set<>::value_type
12#include <unordered_map> // for unordered_map, unordered_map<>...
13#include <unordered_set> // for unordered_set
14#include <utility> // for pair
15
16#include "AST/DeclReplacer.hpp"
17#include "AST/Expr.hpp"
18#include "AST/Inspect.hpp" // for getFunction, getPointerBase, g...
19#include "AST/Node.hpp"
20#include "AST/Pass.hpp"
21#include "AST/Print.hpp"
22#include "AST/SymbolTable.hpp"
23#include "AST/Type.hpp"
24#include "CodeGen/GenType.h" // for genPrettyType
25#include "CodeGen/OperatorTable.h"
26#include "Common/PassVisitor.h" // for PassVisitor, WithStmtsToAdd
27#include "Common/SemanticError.h" // for SemanticError
28#include "Common/ToString.hpp" // for toCString
29#include "Common/UniqueName.h" // for UniqueName
30#include "FixGlobalInit.h" // for fixGlobalInit
31#include "GenInit.h" // for genCtorDtor
32#include "GenPoly/GenPoly.h" // for getFunctionType
33#include "ResolvExpr/Resolver.h" // for findVoidExpression
34#include "ResolvExpr/Unify.h" // for typesCompatible
35#include "SymTab/Autogen.h" // for genImplicitCall
36#include "SymTab/GenImplicitCall.hpp" // for genImplicitCall
37#include "SymTab/Indexer.h" // for Indexer
38#include "SymTab/Mangler.h" // for Mangler
39#include "SynTree/LinkageSpec.h" // for C, Spec, Cforall, isBuiltin
40#include "SynTree/Attribute.h" // for Attribute
41#include "SynTree/Constant.h" // for Constant
42#include "SynTree/Declaration.h" // for ObjectDecl, FunctionDecl, Decl...
43#include "SynTree/Expression.h" // for UniqueExpr, VariableExpr, Unty...
44#include "SynTree/Initializer.h" // for ConstructorInit, SingleInit
45#include "SynTree/Label.h" // for Label, operator<
46#include "SynTree/Mutator.h" // for mutateAll, Mutator, maybeMutate
47#include "SynTree/Statement.h" // for ExprStmt, CompoundStmt, Branch...
48#include "SynTree/Type.h" // for Type, Type::StorageClasses
49#include "SynTree/TypeSubstitution.h" // for TypeSubstitution, operator<<
50#include "SynTree/DeclReplacer.h" // for DeclReplacer
51#include "SynTree/Visitor.h" // for acceptAll, maybeAccept
52#include "Validate/FindSpecialDecls.h" // for dtorStmt, dtorStructDestroy
53
54extern bool ctordtorp; // print all debug
55extern bool ctorp; // print ctor debug
56extern bool cpctorp; // print copy ctor debug
57extern bool dtorp; // print dtor debug
58#define PRINT( text ) if ( ctordtorp ) { text }
59#define CP_CTOR_PRINT( text ) if ( ctordtorp || cpctorp ) { text }
60#define DTOR_PRINT( text ) if ( ctordtorp || dtorp ) { text }
61
62namespace InitTweak {
63namespace {
64
65 // Shallow copy the pointer list for return.
66 std::vector<ast::ptr<ast::TypeDecl>> getGenericParams( const ast::Type * t ) {
67 if ( auto inst = dynamic_cast<const ast::StructInstType *>( t ) ) {
68 return inst->base->params;
69 }
70 if ( auto inst = dynamic_cast<const ast::UnionInstType *>( t ) ) {
71 return inst->base->params;
72 }
73 return {};
74 }
75
76 /// Given type T, generate type of default ctor/dtor, i.e. function type void (*) (T &).
77 ast::FunctionDecl * genDefaultFunc(
78 const CodeLocation loc,
79 const std::string fname,
80 const ast::Type * paramType,
81 bool maybePolymorphic = true) {
82 std::vector<ast::ptr<ast::TypeDecl>> typeParams;
83 if ( maybePolymorphic ) typeParams = getGenericParams( paramType );
84 auto dstParam = new ast::ObjectDecl( loc,
85 "_dst",
86 new ast::ReferenceType( paramType ),
87 nullptr,
88 {},
89 ast::Linkage::Cforall
90 );
91 return new ast::FunctionDecl( loc,
92 fname,
93 std::move(typeParams),
94 {dstParam},
95 {},
96 new ast::CompoundStmt(loc),
97 {},
98 ast::Linkage::Cforall
99 );
100 }
101
102 struct SelfAssignChecker {
103 void previsit( const ast::ApplicationExpr * appExpr );
104 };
105
106 struct StmtExprResult {
107 const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
108 };
109
110 /// wrap function application expressions as ImplicitCopyCtorExpr nodes so that it is easy to identify which
111 /// function calls need their parameters to be copy constructed
112 struct InsertImplicitCalls : public ast::WithShortCircuiting {
113 const ast::Expr * postvisit( const ast::ApplicationExpr * appExpr );
114
115 // only handles each UniqueExpr once
116 // if order of visit does not change, this should be safe
117 void previsit (const ast::UniqueExpr *);
118
119 std::unordered_set<decltype(ast::UniqueExpr::id)> visitedIds;
120 };
121
122 /// generate temporary ObjectDecls for each argument and return value of each ImplicitCopyCtorExpr,
123 /// generate/resolve copy construction expressions for each, and generate/resolve destructors for both
124 /// arguments and return value temporaries
125 struct ResolveCopyCtors final : public ast::WithGuards, public ast::WithStmtsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithVisitorRef<ResolveCopyCtors>, public ast::WithConstTranslationUnit {
126 const ast::Expr * postvisit( const ast::ImplicitCopyCtorExpr * impCpCtorExpr );
127 const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
128 const ast::UniqueExpr * previsit( const ast::UniqueExpr * unqExpr );
129
130 /// handles distant mutations of environment manually.
131 /// WithConstTypeSubstitution cannot remember where the environment is from
132
133 /// MUST be called at start of overload previsit
134 void previsit( const ast::Expr * expr);
135 /// MUST be called at return of overload postvisit
136 const ast::Expr * postvisit(const ast::Expr * expr);
137
138 /// create and resolve ctor/dtor expression: fname(var, [cpArg])
139 const ast::Expr * makeCtorDtor( const std::string & fname, const ast::ObjectDecl * var, const ast::Expr * cpArg = nullptr );
140 /// true if type does not need to be copy constructed to ensure correctness
141 bool skipCopyConstruct( const ast::Type * type );
142 ast::ptr< ast::Expr > copyConstructArg( const ast::Expr * arg, const ast::ImplicitCopyCtorExpr * impCpCtorExpr, const ast::Type * formal );
143 ast::Expr * destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg );
144 private:
145 /// hack to implement WithTypeSubstitution while conforming to mutation safety.
146 ast::TypeSubstitution * env = nullptr;
147 bool envModified = false;
148 };
149
150 /// collects constructed object decls - used as a base class
151 struct ObjDeclCollector : public ast::WithGuards, public ast::WithShortCircuiting {
152 // use ordered data structure to maintain ordering for set_difference and for consistent error messages
153 typedef std::list< const ast::ObjectDecl * > ObjectSet;
154 void previsit( const ast::CompoundStmt *compoundStmt );
155 void previsit( const ast::DeclStmt *stmt );
156
157 // don't go into other functions
158 void previsit( const ast::FunctionDecl * ) { visit_children = false; }
159
160 protected:
161 ObjectSet curVars;
162 };
163
164 // debug
165 template<typename ObjectSet>
166 struct PrintSet {
167 PrintSet( const ObjectSet & objs ) : objs( objs ) {}
168 const ObjectSet & objs;
169 };
170 template<typename ObjectSet>
171 PrintSet<ObjectSet> printSet( const ObjectSet & objs ) { return PrintSet<ObjectSet>( objs ); }
172 template<typename ObjectSet>
173 std::ostream & operator<<( std::ostream & out, const PrintSet<ObjectSet> & set) {
174 out << "{ ";
175 for ( auto & obj : set.objs ) {
176 out << obj->name << ", " ;
177 } // for
178 out << " }";
179 return out;
180 }
181
182 struct LabelFinder final : public ObjDeclCollector {
183 typedef std::map< std::string, ObjectSet > LabelMap;
184 // map of Label -> live variables at that label
185 LabelMap vars;
186
187 typedef ObjDeclCollector Parent;
188 using Parent::previsit;
189 void previsit( const ast::Stmt * stmt );
190
191 void previsit( const ast::CompoundStmt *compoundStmt );
192 void previsit( const ast::DeclStmt *stmt );
193 };
194
195 /// insert destructor calls at the appropriate places. must happen before CtorInit nodes are removed
196 /// (currently by FixInit)
197 struct InsertDtors final : public ObjDeclCollector, public ast::WithStmtsToAdd<> {
198 typedef std::list< ObjectDecl * > OrderedDecls;
199 typedef std::list< OrderedDecls > OrderedDeclsStack;
200
201 InsertDtors( ast::Pass<LabelFinder> & finder ) : finder( finder ), labelVars( finder.core.vars ) {}
202
203 typedef ObjDeclCollector Parent;
204 using Parent::previsit;
205
206 void previsit( const ast::FunctionDecl * funcDecl );
207
208 void previsit( const ast::BranchStmt * stmt );
209 private:
210 void handleGoto( const ast::BranchStmt * stmt );
211
212 ast::Pass<LabelFinder> & finder;
213 LabelFinder::LabelMap & labelVars;
214 OrderedDeclsStack reverseDeclOrder;
215 };
216
217 /// expand each object declaration to use its constructor after it is declared.
218 struct FixInit : public ast::WithStmtsToAdd<> {
219 static void fixInitializers( ast::TranslationUnit &translationUnit );
220
221 const ast::DeclWithType * postvisit( const ast::ObjectDecl *objDecl );
222
223 std::list< ast::ptr< ast::Decl > > staticDtorDecls;
224 };
225
226 /// generate default/copy ctor and dtor calls for user-defined struct ctor/dtors
227 /// for any member that is missing a corresponding ctor/dtor call.
228 /// error if a member is used before constructed
229 struct GenStructMemberCalls final : public ast::WithGuards, public ast::WithShortCircuiting, public ast::WithSymbolTable, public ast::WithVisitorRef<GenStructMemberCalls>, public ast::WithConstTranslationUnit {
230 void previsit( const ast::FunctionDecl * funcDecl );
231 const ast::DeclWithType * postvisit( const ast::FunctionDecl * funcDecl );
232
233 void previsit( const ast::MemberExpr * memberExpr );
234 void previsit( const ast::ApplicationExpr * appExpr );
235
236 /// Note: this post mutate used to be in a separate visitor. If this pass breaks, one place to examine is whether it is
237 /// okay for this part of the recursion to occur alongside the rest.
238 const ast::Expr * postvisit( const ast::UntypedExpr * expr );
239
240 SemanticErrorException errors;
241 private:
242 template< typename... Params >
243 void emit( CodeLocation, const Params &... params );
244
245 ast::FunctionDecl * function = nullptr;
246 std::set< const ast::DeclWithType * > unhandled;
247 std::map< const ast::DeclWithType *, CodeLocation > usedUninit;
248 const ast::ObjectDecl * thisParam = nullptr;
249 bool isCtor = false; // true if current function is a constructor
250 const ast::StructDecl * structDecl = nullptr;
251 };
252
253 /// expands ConstructorExpr nodes into comma expressions, using a temporary for the first argument
254 struct FixCtorExprs final : public ast::WithDeclsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithConstTranslationUnit {
255 const ast::Expr * postvisit( const ast::ConstructorExpr * ctorExpr );
256 };
257
258 /// add CompoundStmts around top-level expressions so that temporaries are destroyed in the correct places.
259 struct SplitExpressions : public ast::WithShortCircuiting {
260 ast::Stmt * postvisit( const ast::ExprStmt * stmt );
261 void previsit( const ast::TupleAssignExpr * expr );
262 };
263} // namespace
264
265void fix( ast::TranslationUnit & translationUnit, bool inLibrary ) {
266 ast::Pass<SelfAssignChecker>::run( translationUnit );
267
268 // fixes StmtExpr to properly link to their resulting expression
269 ast::Pass<StmtExprResult>::run( translationUnit );
270
271 // fixes ConstructorInit for global variables. should happen before fixInitializers.
272 InitTweak::fixGlobalInit( translationUnit, inLibrary );
273
274 // must happen before ResolveCopyCtors because temporaries have to be inserted into the correct scope
275 ast::Pass<SplitExpressions>::run( translationUnit );
276
277 ast::Pass<InsertImplicitCalls>::run( translationUnit );
278
279 // Needs to happen before ResolveCopyCtors, because argument/return temporaries should not be considered in
280 // error checking branch statements
281 {
282 ast::Pass<LabelFinder> finder;
283 ast::Pass<InsertDtors>::run( translationUnit, finder );
284 }
285
286 ast::Pass<ResolveCopyCtors>::run( translationUnit );
287 FixInit::fixInitializers( translationUnit );
288 ast::Pass<GenStructMemberCalls>::run( translationUnit );
289
290 // Needs to happen after GenStructMemberCalls, since otherwise member constructors exprs
291 // don't have the correct form, and a member can be constructed more than once.
292 ast::Pass<FixCtorExprs>::run( translationUnit );
293}
294
295namespace {
296 /// find and return the destructor used in `input`. If `input` is not a simple destructor call, generate a thunk
297 /// that wraps the destructor, insert it into `stmtsToAdd` and return the new function declaration
298 const ast::DeclWithType * getDtorFunc( const ast::ObjectDecl * objDecl, const ast::Stmt * input, std::list< ast::ptr<ast::Stmt> > & stmtsToAdd ) {
299 const CodeLocation loc = input->location;
300 // unwrap implicit statement wrapper
301 // Statement * dtor = input;
302 assert( input );
303 // std::list< const ast::Expr * > matches;
304 auto matches = collectCtorDtorCalls( input );
305
306 if ( dynamic_cast< const ast::ExprStmt * >( input ) ) {
307 // only one destructor call in the expression
308 if ( matches.size() == 1 ) {
309 auto func = getFunction( matches.front() );
310 assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
311
312 // cleanup argument must be a function, not an object (including function pointer)
313 if ( auto dtorFunc = dynamic_cast< const ast::FunctionDecl * > ( func ) ) {
314 if ( dtorFunc->type->forall.empty() ) {
315 // simple case where the destructor is a monomorphic function call - can simply
316 // use that function as the cleanup function.
317 return func;
318 }
319 }
320 }
321 }
322
323 // otherwise the cleanup is more complicated - need to build a single argument cleanup function that
324 // wraps the more complicated code.
325 static UniqueName dtorNamer( "__cleanup_dtor" );
326 std::string name = dtorNamer.newName();
327 ast::FunctionDecl * dtorFunc = genDefaultFunc( loc, name, objDecl->type->stripReferences(), false );
328 stmtsToAdd.push_back( new ast::DeclStmt(loc, dtorFunc ) );
329
330 // the original code contains uses of objDecl - replace them with the newly generated 'this' parameter.
331 const ast::ObjectDecl * thisParam = getParamThis( dtorFunc );
332 const ast::Expr * replacement = new ast::VariableExpr( loc, thisParam );
333
334 auto base = replacement->result->stripReferences();
335 if ( dynamic_cast< const ast::ArrayType * >( base ) || dynamic_cast< const ast::TupleType * > ( base ) ) {
336 // need to cast away reference for array types, since the destructor is generated without the reference type,
337 // and for tuple types since tuple indexing does not work directly on a reference
338 replacement = new ast::CastExpr( replacement, base );
339 }
340 auto dtor = ast::DeclReplacer::replace( input, ast::DeclReplacer::ExprMap{ std::make_pair( objDecl, replacement ) } );
341 auto mutStmts = dtorFunc->stmts.get_and_mutate();
342 mutStmts->push_back(strict_dynamic_cast<const ast::Stmt *>( dtor ));
343 dtorFunc->stmts = mutStmts;
344
345 return dtorFunc;
346 }
347
348 void FixInit::fixInitializers( ast::TranslationUnit & translationUnit ) {
349 ast::Pass<FixInit> fixer;
350
351 // can't use mutateAll, because need to insert declarations at top-level
352 // can't use DeclMutator, because sometimes need to insert IfStmt, etc.
353 SemanticErrorException errors;
354 for ( auto i = translationUnit.decls.begin(); i != translationUnit.decls.end(); ++i ) {
355 try {
356 // maybeAccept( *i, fixer ); translationUnit should never contain null
357 *i = (*i)->accept(fixer);
358 translationUnit.decls.splice( i, fixer.core.staticDtorDecls );
359 } catch( SemanticErrorException &e ) {
360 errors.append( e );
361 } // try
362 } // for
363 if ( ! errors.isEmpty() ) {
364 throw errors;
365 } // if
366 }
367
368 const ast::StmtExpr * StmtExprResult::previsit( const ast::StmtExpr * stmtExpr ) {
369 // we might loose the result expression here so add a pointer to trace back
370 assert( stmtExpr->result );
371 const ast::Type * result = stmtExpr->result;
372 if ( ! result->isVoid() ) {
373 auto mutExpr = mutate(stmtExpr);
374 const ast::CompoundStmt * body = mutExpr->stmts;
375 assert( ! body->kids.empty() );
376 mutExpr->resultExpr = body->kids.back().strict_as<ast::ExprStmt>();
377 return mutExpr;
378 }
379 return stmtExpr;
380 }
381
382 ast::Stmt * SplitExpressions::postvisit( const ast::ExprStmt * stmt ) {
383 // wrap each top-level ExprStmt in a block so that destructors for argument and return temporaries are destroyed
384 // in the correct places
385 ast::CompoundStmt * ret = new ast::CompoundStmt( stmt->location, { stmt } );
386 return ret;
387 }
388
389 void SplitExpressions::previsit( const ast::TupleAssignExpr * ) {
390 // don't do this within TupleAssignExpr, since it is already broken up into multiple expressions
391 visit_children = false;
392 }
393
394 // Relatively simple structural comparison for expressions, needed to determine
395 // if two expressions are "the same" (used to determine if self assignment occurs)
396 struct StructuralChecker {
397 // Strip all casts and then dynamic_cast.
398 template<typename T>
399 static const T * cast( const ast::Expr * expr ) {
400 // this might be too permissive. It's possible that only particular casts are relevant.
401 while ( auto cast = dynamic_cast< const ast::CastExpr * >( expr ) ) {
402 expr = cast->arg;
403 }
404 return dynamic_cast< const T * >( expr );
405 }
406
407 void previsit( const ast::Expr * ) {
408 // anything else does not qualify
409 result = false;
410 }
411
412 // ignore casts
413 void previsit( const ast::CastExpr * ) {}
414
415 void previsit( const ast::MemberExpr * memExpr ) {
416 if ( auto otherMember = cast< ast::MemberExpr >( other ) ) {
417 if ( otherMember->member == memExpr->member ) {
418 other = otherMember->aggregate;
419 return;
420 }
421 }
422 result = false;
423 }
424
425 void previsit( const ast::VariableExpr * varExpr ) {
426 if ( auto otherVar = cast< ast::VariableExpr >( other ) ) {
427 if ( otherVar->var == varExpr->var ) {
428 return;
429 }
430 }
431 result = false;
432 }
433
434 void previsit( const ast::AddressExpr * ) {
435 if ( auto addrExpr = cast< ast::AddressExpr >( other ) ) {
436 other = addrExpr->arg;
437 return;
438 }
439 result = false;
440 }
441
442 const ast::Expr * other;
443 bool result = true;
444 StructuralChecker( const ast::Expr * other ) : other(other) {}
445 };
446
447 bool structurallySimilar( const ast::Expr * e1, const ast::Expr * e2 ) {
448 return ast::Pass<StructuralChecker>::read( e1, e2 );
449 }
450
451 void SelfAssignChecker::previsit( const ast::ApplicationExpr * appExpr ) {
452 auto function = getFunction( appExpr );
453 // Doesn't use isAssignment, because ?+=?, etc. should not count as self-assignment.
454 if ( function->name == "?=?" && appExpr->args.size() == 2
455 // Check for structural similarity (same variable use, ignore casts, etc.
456 // (but does not look too deeply, anything looking like a function is off limits).
457 && structurallySimilar( appExpr->args.front(), appExpr->args.back() ) ) {
458 SemanticWarning( appExpr->location, Warning::SelfAssignment, toCString( appExpr->args.front() ) );
459 }
460 }
461
462 const ast::Expr * InsertImplicitCalls::postvisit( const ast::ApplicationExpr * appExpr ) {
463 if ( auto function = appExpr->func.as<ast::VariableExpr>() ) {
464 if ( function->var->linkage.is_builtin ) {
465 // optimization: don't need to copy construct in order to call intrinsic functions
466 return appExpr;
467 } else if ( auto funcDecl = function->var.as<ast::DeclWithType>() ) {
468 auto ftype = dynamic_cast< const ast::FunctionType * >( GenPoly::getFunctionType( funcDecl->get_type() ) );
469 assertf( ftype, "Function call without function type: %s", toString( funcDecl ).c_str() );
470 if ( CodeGen::isConstructor( funcDecl->name ) && ftype->params.size() == 2 ) {
471 auto t1 = getPointerBase( ftype->params.front() );
472 auto t2 = ftype->params.back();
473 assert( t1 );
474
475 if ( ResolvExpr::typesCompatible( t1, t2 ) ) {
476 // optimization: don't need to copy construct in order to call a copy constructor
477 return appExpr;
478 } // if
479 } else if ( CodeGen::isDestructor( funcDecl->name ) ) {
480 // correctness: never copy construct arguments to a destructor
481 return appExpr;
482 } // if
483 } // if
484 } // if
485 CP_CTOR_PRINT( std::cerr << "InsertImplicitCalls: adding a wrapper " << appExpr << std::endl; )
486
487 // wrap each function call so that it is easy to identify nodes that have to be copy constructed
488 ast::ptr<ast::TypeSubstitution> tmp = appExpr->env;
489 auto mutExpr = mutate(appExpr);
490 mutExpr->env = nullptr;
491
492 auto expr = new ast::ImplicitCopyCtorExpr( appExpr->location, mutExpr );
493 // Move the type substitution to the new top-level. The substitution
494 // is needed to obtain the type of temporary variables so that copy
495 // constructor calls can be resolved.
496 expr->env = tmp;
497 return expr;
498 }
499
500 void ResolveCopyCtors::previsit(const ast::Expr * expr) {
501 if ( nullptr == expr->env ) {
502 return;
503 }
504 GuardValue( env ) = expr->env->clone();
505 GuardValue( envModified ) = false;
506 }
507
508 const ast::Expr * ResolveCopyCtors::postvisit(const ast::Expr * expr) {
509 // No local environment, skip.
510 if ( nullptr == expr->env ) {
511 return expr;
512 // Environment was modified, mutate and replace.
513 } else if ( envModified ) {
514 auto mutExpr = mutate(expr);
515 mutExpr->env = env;
516 return mutExpr;
517 // Environment was not mutated, delete the shallow copy before guard.
518 } else {
519 delete env;
520 return expr;
521 }
522 }
523
524 bool ResolveCopyCtors::skipCopyConstruct( const ast::Type * type ) { return ! isConstructable( type ); }
525
526 const ast::Expr * ResolveCopyCtors::makeCtorDtor( const std::string & fname, const ast::ObjectDecl * var, const ast::Expr * cpArg ) {
527 assert( var );
528 assert( var->isManaged() );
529 assert( !cpArg || cpArg->isManaged() );
530 // arrays are not copy constructed, so this should always be an ExprStmt
531 ast::ptr< ast::Stmt > stmt = genCtorDtor(var->location, fname, var, cpArg );
532 assertf( stmt, "ResolveCopyCtors: genCtorDtor returned nullptr: %s / %s / %s", fname.c_str(), toString( var ).c_str(), toString( cpArg ).c_str() );
533 auto exprStmt = stmt.strict_as<ast::ImplicitCtorDtorStmt>()->callStmt.strict_as<ast::ExprStmt>();
534 ast::ptr<ast::Expr> untyped = exprStmt->expr; // take ownership of expr
535
536 // resolve copy constructor
537 // should only be one alternative for copy ctor and dtor expressions, since all arguments are fixed
538 // (VariableExpr and already resolved expression)
539 CP_CTOR_PRINT( std::cerr << "ResolvingCtorDtor " << untyped << std::endl; )
540 ast::ptr<ast::Expr> resolved = ResolvExpr::findVoidExpression(untyped, { symtab, transUnit().global } );
541 assert( resolved );
542 if ( resolved->env ) {
543 // Extract useful information and discard new environments. Keeping them causes problems in PolyMutator passes.
544 env->add( *resolved->env );
545 envModified = true;
546 auto mut = mutate(resolved.get());
547 assertf(mut == resolved.get(), "newly resolved expression must be unique");
548 mut->env = nullptr;
549 } // if
550 if ( auto assign = resolved.as<ast::TupleAssignExpr>() ) {
551 // fix newly generated StmtExpr
552 previsit( assign->stmtExpr );
553 }
554 return resolved.release();
555 }
556
557 ast::ptr<ast::Expr> ResolveCopyCtors::copyConstructArg(
558 const ast::Expr * arg, const ast::ImplicitCopyCtorExpr * impCpCtorExpr, const ast::Type * formal )
559 {
560 static UniqueName tempNamer("_tmp_cp");
561 const CodeLocation loc = impCpCtorExpr->location;
562 // CP_CTOR_PRINT( std::cerr << "Type Substitution: " << *env << std::endl; )
563 assert( arg->result );
564 ast::ptr<ast::Type> result = arg->result;
565 if ( skipCopyConstruct( result ) ) return arg; // skip certain non-copyable types
566
567 // type may involve type variables, so apply type substitution to get temporary variable's actual type,
568 // since result type may not be substituted (e.g., if the type does not appear in the parameter list)
569 // Use applyFree so that types bound in function pointers are not substituted, e.g. in forall(dtype T) void (*)(T).
570
571 // xxx - this originally mutates arg->result in place. is it correct?
572 assert( env );
573 result = env->applyFree( result.get() ).node;
574 auto mutResult = result.get_and_mutate();
575 mutResult->set_const(false);
576
577 auto mutArg = mutate(arg);
578 mutArg->result = mutResult;
579
580 ast::ptr<ast::Expr> guard = mutArg;
581
582 ast::ptr<ast::ObjectDecl> tmp = new ast::ObjectDecl(loc, "__tmp", mutResult, nullptr );
583
584 // create and resolve copy constructor
585 CP_CTOR_PRINT( std::cerr << "makeCtorDtor for an argument" << std::endl; )
586 auto cpCtor = makeCtorDtor( "?{}", tmp, mutArg );
587
588 if ( auto appExpr = dynamic_cast< const ast::ApplicationExpr * >( cpCtor ) ) {
589 // if the chosen constructor is intrinsic, the copy is unnecessary, so
590 // don't create the temporary and don't call the copy constructor
591 auto function = appExpr->func.strict_as<ast::VariableExpr>();
592 if ( function->var->linkage == ast::Linkage::Intrinsic ) {
593 // arguments that need to be boxed need a temporary regardless of whether the copy constructor is intrinsic,
594 // so that the object isn't changed inside of the polymorphic function
595 if ( ! GenPoly::needsBoxing( formal, result, impCpCtorExpr->callExpr, env ) ) {
596 // xxx - should arg->result be mutated? see comment above.
597 return guard;
598 }
599 }
600 }
601
602 // set a unique name for the temporary once it's certain the call is necessary
603 auto mut = tmp.get_and_mutate();
604 assertf (mut == tmp, "newly created ObjectDecl must be unique");
605 mut->name = tempNamer.newName();
606
607 // replace argument to function call with temporary
608 stmtsToAddBefore.push_back( new ast::DeclStmt(loc, tmp ) );
609 arg = cpCtor;
610 return destructRet( tmp, arg );
611
612 // impCpCtorExpr->dtors.push_front( makeCtorDtor( "^?{}", tmp ) );
613 }
614
615 ast::Expr * ResolveCopyCtors::destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg ) {
616 auto global = transUnit().global;
617 // TODO: refactor code for generating cleanup attribute, since it's common and reused in ~3-4 places
618 // check for existing cleanup attribute before adding another(?)
619 // need to add __Destructor for _tmp_cp variables as well
620
621 assertf( global.dtorStruct, "Destructor generation requires __Destructor definition." );
622 assertf( global.dtorStruct->members.size() == 2, "__Destructor definition does not have expected fields." );
623 assertf( global.dtorDestroy, "Destructor generation requires __destroy_Destructor." );
624
625 const CodeLocation loc = ret->location;
626
627 // generate a __Destructor for ret that calls the destructor
628 auto res = makeCtorDtor( "^?{}", ret );
629 auto dtor = mutate(res);
630
631 // if the chosen destructor is intrinsic, elide the generated dtor handler
632 if ( arg && isIntrinsicCallExpr( dtor ) ) {
633 return new ast::CommaExpr(loc, arg, new ast::VariableExpr(loc, ret ) );
634 }
635
636 if ( ! dtor->env ) dtor->env = maybeClone( env );
637 auto dtorFunc = getDtorFunc( ret, new ast::ExprStmt(loc, dtor ), stmtsToAddBefore );
638
639 auto dtorStructType = new ast::StructInstType( global.dtorStruct );
640
641 // what does this do???
642 dtorStructType->params.push_back( new ast::TypeExpr(loc, new ast::VoidType() ) );
643
644 // cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
645 auto dtorFtype = new ast::FunctionType();
646 dtorFtype->params.push_back( new ast::PointerType(new ast::VoidType( ) ) );
647 auto dtorType = new ast::PointerType( dtorFtype );
648
649 static UniqueName namer( "_ret_dtor" );
650 auto retDtor = new ast::ObjectDecl(loc, namer.newName(), dtorStructType, new ast::ListInit(loc, { new ast::SingleInit(loc, ast::ConstantExpr::null(loc) ), new ast::SingleInit(loc, new ast::CastExpr( new ast::VariableExpr(loc, dtorFunc ), dtorType ) ) } ) );
651 retDtor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, global.dtorDestroy ) } ) );
652 stmtsToAddBefore.push_back( new ast::DeclStmt(loc, retDtor ) );
653
654 if ( arg ) {
655 auto member = new ast::MemberExpr(loc, global.dtorStruct->members.front().strict_as<ast::DeclWithType>(), new ast::VariableExpr(loc, retDtor ) );
656 auto object = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, ret ) ), new ast::PointerType(new ast::VoidType() ) );
657 ast::Expr * assign = createBitwiseAssignment( member, object );
658 return new ast::CommaExpr(loc, new ast::CommaExpr(loc, arg, assign ), new ast::VariableExpr(loc, ret ) );
659 }
660 return nullptr;
661 // impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", ret ) );
662 }
663
664 const ast::Expr * ResolveCopyCtors::postvisit( const ast::ImplicitCopyCtorExpr *impCpCtorExpr ) {
665 CP_CTOR_PRINT( std::cerr << "ResolveCopyCtors: " << impCpCtorExpr << std::endl; )
666
667 ast::ApplicationExpr * appExpr = mutate(impCpCtorExpr->callExpr.get());
668 const ast::ObjectDecl * returnDecl = nullptr;
669 const CodeLocation loc = appExpr->location;
670
671 // take each argument and attempt to copy construct it.
672 auto ftype = GenPoly::getFunctionType( appExpr->func->result );
673 assert( ftype );
674 auto & params = ftype->params;
675 auto iter = params.begin();
676 for ( auto & arg : appExpr->args ) {
677 const ast::Type * formal = nullptr;
678 if ( iter != params.end() ) { // does not copy construct C-style variadic arguments
679 // DeclarationWithType * param = *iter++;
680 formal = *iter++;
681 }
682
683 arg = copyConstructArg( arg, impCpCtorExpr, formal );
684 } // for
685
686 // each return value from the call needs to be connected with an ObjectDecl at the call site, which is
687 // initialized with the return value and is destructed later
688 // xxx - handle named return values?
689 const ast::Type * result = appExpr->result;
690 if ( ! result->isVoid() ) {
691 static UniqueName retNamer("_tmp_cp_ret");
692 auto subResult = env->apply( result ).node;
693 auto ret = new ast::ObjectDecl(loc, retNamer.newName(), subResult, nullptr );
694 auto mutType = mutate(ret->type.get());
695 mutType->set_const( false );
696 ret->type = mutType;
697 returnDecl = ret;
698 stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
699 CP_CTOR_PRINT( std::cerr << "makeCtorDtor for a return" << std::endl; )
700 } // for
701 CP_CTOR_PRINT( std::cerr << "after Resolving: " << impCpCtorExpr << std::endl; )
702 // ------------------------------------------------------
703
704 CP_CTOR_PRINT( std::cerr << "Coming out the back..." << impCpCtorExpr << std::endl; )
705
706 // detach fields from wrapper node so that it can be deleted without deleting too much
707
708 // xxx - actual env might be somewhere else, need to keep invariant
709
710 // deletion of wrapper should be handled by pass template now
711
712 // impCpCtorExpr->callExpr = nullptr;
713 assert (appExpr->env == nullptr);
714 appExpr->env = impCpCtorExpr->env;
715 // std::swap( impCpCtorExpr->env, appExpr->env );
716 // assert( impCpCtorExpr->env == nullptr );
717 // delete impCpCtorExpr;
718
719 if ( returnDecl ) {
720 ast::Expr * assign = createBitwiseAssignment( new ast::VariableExpr(loc, returnDecl ), appExpr );
721 if ( ! dynamic_cast< const ast::ReferenceType * >( result ) ) {
722 // destructing reference returns is bad because it can cause multiple destructor calls to the same object - the returned object is not a temporary
723 assign = destructRet( returnDecl, assign );
724 assert(assign);
725 } else {
726 assign = new ast::CommaExpr(loc, assign, new ast::VariableExpr(loc, returnDecl ) );
727 }
728 // move env from appExpr to retExpr
729 // std::swap( assign->env, appExpr->env );
730 assign->env = appExpr->env;
731 // actual env is handled by common routine that replaces WithTypeSubstitution
732 return postvisit((const ast::Expr *)assign);
733 } else {
734 return postvisit((const ast::Expr *)appExpr);
735 } // if
736 }
737
738 const ast::StmtExpr * ResolveCopyCtors::previsit( const ast::StmtExpr * _stmtExpr ) {
739 // function call temporaries should be placed at statement-level, rather than nested inside of a new statement expression,
740 // since temporaries can be shared across sub-expressions, e.g.
741 // [A, A] f(); // decl
742 // g([A] x, [A] y); // decl
743 // g(f()); // call
744 // f is executed once, so the return temporary is shared across the tuple constructors for x and y.
745 // Explicitly mutating children instead of mutating the inner compound statement forces the temporaries to be added
746 // to the outer context, rather than inside of the statement expression.
747
748 // call the common routine that replaces WithTypeSubstitution
749 previsit((const ast::Expr *) _stmtExpr);
750
751 visit_children = false;
752 const CodeLocation loc = _stmtExpr->location;
753
754 assert( env );
755
756 symtab.enterScope();
757 // visit all statements
758 auto stmtExpr = mutate(_stmtExpr);
759 auto mutStmts = mutate(stmtExpr->stmts.get());
760
761 auto & stmts = mutStmts->kids;
762 for ( auto & stmt : stmts ) {
763 stmt = stmt->accept( *visitor );
764 } // for
765 stmtExpr->stmts = mutStmts;
766 symtab.leaveScope();
767
768 assert( stmtExpr->result );
769 // const ast::Type * result = stmtExpr->result;
770 if ( ! stmtExpr->result->isVoid() ) {
771 static UniqueName retNamer("_tmp_stmtexpr_ret");
772
773 // result = result->clone();
774 auto result = env->apply( stmtExpr->result.get() ).node;
775 if ( ! InitTweak::isConstructable( result ) ) {
776 // delete result;
777 return stmtExpr;
778 }
779 auto mutResult = result.get_and_mutate();
780 mutResult->set_const(false);
781
782 // create variable that will hold the result of the stmt expr
783 auto ret = new ast::ObjectDecl(loc, retNamer.newName(), mutResult, nullptr );
784 stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
785
786 assertf(
787 stmtExpr->resultExpr,
788 "Statement-Expression should have a resulting expression at %s:%d",
789 stmtExpr->location.filename.c_str(),
790 stmtExpr->location.first_line
791 );
792
793 const ast::ExprStmt * last = stmtExpr->resultExpr;
794 // xxx - if this is non-unique, need to copy while making resultExpr ref
795 assertf(last->unique(), "attempt to modify weakly shared statement");
796 auto mutLast = mutate(last);
797 // above assertion means in-place mutation is OK
798 try {
799 mutLast->expr = makeCtorDtor( "?{}", ret, mutLast->expr );
800 } catch(...) {
801 std::cerr << "*CFA internal error: ";
802 std::cerr << "can't resolve implicit constructor";
803 std::cerr << " at " << stmtExpr->location.filename;
804 std::cerr << ":" << stmtExpr->location.first_line << std::endl;
805
806 abort();
807 }
808
809 // add destructors after current statement
810 stmtsToAddAfter.push_back( new ast::ExprStmt(loc, makeCtorDtor( "^?{}", ret ) ) );
811
812 // must have a non-empty body, otherwise it wouldn't have a result
813 assert( ! stmts.empty() );
814
815 // if there is a return decl, add a use as the last statement; will not have return decl on non-constructable returns
816 stmts.push_back( new ast::ExprStmt(loc, new ast::VariableExpr(loc, ret ) ) );
817 } // if
818
819 assert( stmtExpr->returnDecls.empty() );
820 assert( stmtExpr->dtors.empty() );
821
822 return stmtExpr;
823 }
824
825 // to prevent warnings ('_unq0' may be used uninitialized in this function),
826 // insert an appropriate zero initializer for UniqueExpr temporaries.
827 ast::Init * makeInit( const ast::Type * t, CodeLocation const & loc ) {
828 if ( auto inst = dynamic_cast< const ast::StructInstType * >( t ) ) {
829 // initizer for empty struct must be empty
830 if ( inst->base->members.empty() ) {
831 return new ast::ListInit( loc, {} );
832 }
833 } else if ( auto inst = dynamic_cast< const ast::UnionInstType * >( t ) ) {
834 // initizer for empty union must be empty
835 if ( inst->base->members.empty() ) {
836 return new ast::ListInit( loc, {} );
837 }
838 }
839
840 return new ast::ListInit( loc, {
841 new ast::SingleInit( loc, ast::ConstantExpr::from_int( loc, 0 ) )
842 } );
843 }
844
845 const ast::UniqueExpr * ResolveCopyCtors::previsit( const ast::UniqueExpr * unqExpr ) {
846 visit_children = false;
847 // xxx - hack to prevent double-handling of unique exprs, otherwise too many temporary variables and destructors are generated
848 static std::unordered_map< int, const ast::UniqueExpr * > unqMap;
849 auto mutExpr = mutate(unqExpr);
850 if ( ! unqMap.count( unqExpr->id ) ) {
851 // resolve expr and find its
852
853 auto impCpCtorExpr = mutExpr->expr.as<ast::ImplicitCopyCtorExpr>();
854 // PassVisitor<ResolveCopyCtors> fixer;
855
856 mutExpr->expr = mutExpr->expr->accept( *visitor );
857 // it should never be necessary to wrap a void-returning expression in a UniqueExpr - if this assumption changes, this needs to be rethought
858 assert( unqExpr->result );
859 if ( impCpCtorExpr ) {
860 auto comma = unqExpr->expr.strict_as<ast::CommaExpr>();
861 auto var = comma->arg2.strict_as<ast::VariableExpr>();
862 // note the variable used as the result from the call
863 mutExpr->var = var;
864 } else {
865 // expr isn't a call expr, so create a new temporary variable to use to hold the value of the unique expression
866 mutExpr->object = new ast::ObjectDecl( mutExpr->location, toString("_unq", mutExpr->id), mutExpr->result, makeInit( mutExpr->result, mutExpr->location ) );
867 mutExpr->var = new ast::VariableExpr( mutExpr->location, mutExpr->object );
868 }
869
870 unqMap[mutExpr->id] = mutExpr;
871 } else {
872 // take data from other UniqueExpr to ensure consistency
873 // delete unqExpr->get_expr();
874 mutExpr->expr = unqMap[mutExpr->id]->expr;
875 // delete unqExpr->result;
876 mutExpr->result = mutExpr->expr->result;
877 }
878 return mutExpr;
879 }
880
881 const ast::DeclWithType * FixInit::postvisit( const ast::ObjectDecl *_objDecl ) {
882 const CodeLocation loc = _objDecl->location;
883
884 // since this removes the init field from objDecl, it must occur after children are mutated (i.e. postvisit)
885 if ( ast::ptr<ast::ConstructorInit> ctorInit = _objDecl->init.as<ast::ConstructorInit>() ) {
886 auto objDecl = mutate(_objDecl);
887
888 // could this be non-unique?
889 if (objDecl != _objDecl) {
890 std::cerr << "FixInit: non-unique object decl " << objDecl->location << objDecl->name << std::endl;
891 }
892 // a decision should have been made by the resolver, so ctor and init are not both non-NULL
893 assert( ! ctorInit->ctor || ! ctorInit->init );
894 if ( const ast::Stmt * ctor = ctorInit->ctor ) {
895 if ( objDecl->storage.is_static ) {
896 addDataSectionAttribute(objDecl);
897 // originally wanted to take advantage of gcc nested functions, but
898 // we get memory errors with this approach. To remedy this, the static
899 // variable is hoisted when the destructor needs to be called.
900 //
901 // generate:
902 // static T __objName_static_varN;
903 // void __objName_dtor_atexitN() {
904 // __dtor__...;
905 // }
906 // int f(...) {
907 // ...
908 // static bool __objName_uninitialized = true;
909 // if (__objName_uninitialized) {
910 // __ctor(__objName);
911 // __objName_uninitialized = false;
912 // atexit(__objName_dtor_atexitN);
913 // }
914 // ...
915 // }
916
917 static UniqueName dtorCallerNamer( "_dtor_atexit" );
918
919 // static bool __objName_uninitialized = true
920 auto boolType = new ast::BasicType( ast::BasicType::Kind::Bool );
921 auto boolInitExpr = new ast::SingleInit(loc, ast::ConstantExpr::from_int(loc, 1 ) );
922 auto isUninitializedVar = new ast::ObjectDecl(loc, objDecl->mangleName + "_uninitialized", boolType, boolInitExpr, ast::Storage::Static, ast::Linkage::Cforall);
923 isUninitializedVar->fixUniqueId();
924
925 // __objName_uninitialized = false;
926 auto setTrue = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ) );
927 setTrue->args.push_back( new ast::VariableExpr(loc, isUninitializedVar ) );
928 setTrue->args.push_back( ast::ConstantExpr::from_int(loc, 0 ) );
929
930 // generate body of if
931 auto initStmts = new ast::CompoundStmt(loc);
932 auto & body = initStmts->kids;
933 body.push_back( ctor );
934 body.push_back( new ast::ExprStmt(loc, setTrue ) );
935
936 // put it all together
937 auto ifStmt = new ast::IfStmt(loc, new ast::VariableExpr(loc, isUninitializedVar ), initStmts, 0 );
938 stmtsToAddAfter.push_back( new ast::DeclStmt(loc, isUninitializedVar ) );
939 stmtsToAddAfter.push_back( ifStmt );
940
941 const ast::Stmt * dtor = ctorInit->dtor;
942
943 // these should be automatically managed once reassigned
944 // objDecl->set_init( nullptr );
945 // ctorInit->set_ctor( nullptr );
946 // ctorInit->set_dtor( nullptr );
947 if ( dtor ) {
948 // if the object has a non-trivial destructor, have to
949 // hoist it and the object into the global space and
950 // call the destructor function with atexit.
951
952 // Statement * dtorStmt = dtor->clone();
953
954 // void __objName_dtor_atexitN(...) {...}
955 ast::FunctionDecl * dtorCaller = new ast::FunctionDecl(loc, objDecl->mangleName + dtorCallerNamer.newName(), {}, {}, {}, new ast::CompoundStmt(loc, {dtor}), ast::Storage::Static, ast::Linkage::C );
956 dtorCaller->fixUniqueId();
957 // dtorCaller->stmts->push_back( dtor );
958
959 // atexit(dtor_atexit);
960 auto callAtexit = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "atexit" ) );
961 callAtexit->args.push_back( new ast::VariableExpr(loc, dtorCaller ) );
962
963 body.push_back( new ast::ExprStmt(loc, callAtexit ) );
964
965 // hoist variable and dtor caller decls to list of decls that will be added into global scope
966 staticDtorDecls.push_back( objDecl );
967 staticDtorDecls.push_back( dtorCaller );
968
969 // need to rename object uniquely since it now appears
970 // at global scope and there could be multiple function-scoped
971 // static variables with the same name in different functions.
972 // Note: it isn't sufficient to modify only the mangleName, because
973 // then subsequent Indexer passes can choke on seeing the object's name
974 // if another object has the same name and type. An unfortunate side-effect
975 // of renaming the object is that subsequent NameExprs may fail to resolve,
976 // but there shouldn't be any remaining past this point.
977 static UniqueName staticNamer( "_static_var" );
978 objDecl->name = objDecl->name + staticNamer.newName();
979 objDecl->mangleName = Mangle::mangle( objDecl );
980 objDecl->init = nullptr;
981
982 // xxx - temporary hack: need to return a declaration, but want to hoist the current object out of this scope
983 // create a new object which is never used
984 static UniqueName dummyNamer( "_dummy" );
985 auto dummy = new ast::ObjectDecl(loc, dummyNamer.newName(), new ast::PointerType(new ast::VoidType()), nullptr, ast::Storage::Static, ast::Linkage::Cforall, 0, { new ast::Attribute("unused") } );
986 // delete ctorInit;
987 return dummy;
988 } else {
989 objDecl->init = nullptr;
990 return objDecl;
991 }
992 } else {
993 auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * > ( ctor );
994 auto ctorStmt = implicit->callStmt.as<ast::ExprStmt>();
995 const ast::ApplicationExpr * ctorCall = nullptr;
996 if ( ctorStmt && (ctorCall = isIntrinsicCallExpr( ctorStmt->expr )) && ctorCall->args.size() == 2 ) {
997 // clean up intrinsic copy constructor calls by making them into SingleInits
998 const ast::Expr * ctorArg = ctorCall->args.back();
999 // ctorCall should be gone afterwards
1000 auto mutArg = mutate(ctorArg);
1001 mutArg->env = ctorCall->env;
1002 // std::swap( ctorArg->env, ctorCall->env );
1003 objDecl->init = new ast::SingleInit(loc, mutArg );
1004
1005 // ctorCall->args.pop_back();
1006 } else {
1007 stmtsToAddAfter.push_back( ctor );
1008 objDecl->init = nullptr;
1009 // ctorInit->ctor = nullptr;
1010 }
1011
1012 const ast::Stmt * dtor = ctorInit->dtor;
1013 if ( dtor ) {
1014 auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * >( dtor );
1015 const ast::Stmt * dtorStmt = implicit->callStmt;
1016
1017 // don't need to call intrinsic dtor, because it does nothing, but
1018 // non-intrinsic dtors must be called
1019 if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {
1020 // set dtor location to the object's location for error messages
1021 auto dtorFunc = getDtorFunc( objDecl, dtorStmt, stmtsToAddBefore );
1022 objDecl->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, dtorFunc ) } ) );
1023 // ctorInit->dtor = nullptr;
1024 } // if
1025 }
1026 } // if
1027 } else if ( const ast::Init * init = ctorInit->init ) {
1028 objDecl->init = init;
1029 // ctorInit->init = nullptr;
1030 } else {
1031 // no constructor and no initializer, which is okay
1032 objDecl->init = nullptr;
1033 } // if
1034 // delete ctorInit;
1035 return objDecl;
1036 } // if
1037 return _objDecl;
1038 }
1039
1040 void ObjDeclCollector::previsit( const ast::CompoundStmt * ) {
1041 GuardValue( curVars );
1042 }
1043
1044 void ObjDeclCollector::previsit( const ast::DeclStmt * stmt ) {
1045 // keep track of all variables currently in scope
1046 if ( auto objDecl = stmt->decl.as<ast::ObjectDecl>() ) {
1047 curVars.push_back( objDecl );
1048 } // if
1049 }
1050
1051 void LabelFinder::previsit( const ast::Stmt * stmt ) {
1052 // for each label, remember the variables in scope at that label.
1053 for ( auto l : stmt->labels ) {
1054 vars[l] = curVars;
1055 } // for
1056 }
1057
1058 void LabelFinder::previsit( const ast::CompoundStmt * stmt ) {
1059 previsit( (const ast::Stmt *) stmt );
1060 Parent::previsit( stmt );
1061 }
1062
1063 void LabelFinder::previsit( const ast::DeclStmt * stmt ) {
1064 previsit( (const ast::Stmt *)stmt );
1065 Parent::previsit( stmt );
1066 }
1067
1068
1069 void InsertDtors::previsit( const ast::FunctionDecl * funcDecl ) {
1070 // each function needs to have its own set of labels
1071 GuardValue( labelVars );
1072 labelVars.clear();
1073 // LabelFinder does not recurse into FunctionDecl, so need to visit
1074 // its children manually.
1075 if (funcDecl->type) funcDecl->type->accept(finder);
1076 // maybeAccept( funcDecl->type, finder );
1077 if (funcDecl->stmts) funcDecl->stmts->accept(finder) ;
1078
1079 // all labels for this function have been collected, insert destructors as appropriate via implicit recursion.
1080 }
1081
1082 // Handle break/continue/goto in the same manner as C++. Basic idea: any objects that are in scope at the
1083 // BranchStmt but not at the labelled (target) statement must be destructed. If there are any objects in scope
1084 // at the target location but not at the BranchStmt then those objects would be uninitialized so notify the user
1085 // of the error. See C++ Reference 6.6 Jump Statements for details.
1086 void InsertDtors::handleGoto( const ast::BranchStmt * stmt ) {
1087 // can't do anything for computed goto
1088 if ( stmt->computedTarget ) return;
1089
1090 assertf( stmt->target.name != "", "BranchStmt missing a label: %s", toString( stmt ).c_str() );
1091 // S_L = lvars = set of objects in scope at label definition
1092 // S_G = curVars = set of objects in scope at goto statement
1093 ObjectSet & lvars = labelVars[ stmt->target ];
1094
1095 DTOR_PRINT(
1096 std::cerr << "at goto label: " << stmt->target.name << std::endl;
1097 std::cerr << "S_G = " << printSet( curVars ) << std::endl;
1098 std::cerr << "S_L = " << printSet( lvars ) << std::endl;
1099 )
1100
1101
1102 // std::set_difference requires that the inputs be sorted.
1103 lvars.sort();
1104 curVars.sort();
1105
1106 ObjectSet diff;
1107 // S_L-S_G results in set of objects whose construction is skipped - it's an error if this set is non-empty
1108 std::set_difference( lvars.begin(), lvars.end(), curVars.begin(), curVars.end(), std::inserter( diff, diff.begin() ) );
1109 DTOR_PRINT(
1110 std::cerr << "S_L-S_G = " << printSet( diff ) << std::endl;
1111 )
1112 if ( ! diff.empty() ) {
1113 SemanticError( stmt, std::string("jump to label '") + stmt->target.name + "' crosses initialization of " + (*diff.begin())->name + " " );
1114 } // if
1115 }
1116
1117 void InsertDtors::previsit( const ast::BranchStmt * stmt ) {
1118 switch( stmt->kind ) {
1119 case ast::BranchStmt::Continue:
1120 case ast::BranchStmt::Break:
1121 // could optimize the break/continue case, because the S_L-S_G check is unnecessary (this set should
1122 // always be empty), but it serves as a small sanity check.
1123 case ast::BranchStmt::Goto:
1124 handleGoto( stmt );
1125 break;
1126 default:
1127 assert( false );
1128 } // switch
1129 }
1130
1131 bool checkWarnings( const ast::FunctionDecl * funcDecl ) {
1132 // only check for warnings if the current function is a user-defined
1133 // constructor or destructor
1134 if ( ! funcDecl ) return false;
1135 if ( ! funcDecl->stmts ) return false;
1136 return CodeGen::isCtorDtor( funcDecl->name ) && ! funcDecl->linkage.is_overrideable;
1137 }
1138
1139 void GenStructMemberCalls::previsit( const ast::FunctionDecl * funcDecl ) {
1140 GuardValue( function );
1141 GuardValue( unhandled );
1142 GuardValue( usedUninit );
1143 GuardValue( thisParam );
1144 GuardValue( isCtor );
1145 GuardValue( structDecl );
1146 errors = SemanticErrorException(); // clear previous errors
1147
1148 // need to start with fresh sets
1149 unhandled.clear();
1150 usedUninit.clear();
1151
1152 function = mutate(funcDecl);
1153 // could this be non-unique?
1154 if (function != funcDecl) {
1155 std::cerr << "GenStructMemberCalls: non-unique FunctionDecl " << funcDecl->location << funcDecl->name << std::endl;
1156 }
1157
1158 isCtor = CodeGen::isConstructor( function->name );
1159 if ( checkWarnings( function ) ) {
1160 // const ast::FunctionType * type = function->type;
1161 // assert( ! type->params.empty() );
1162 thisParam = function->params.front().strict_as<ast::ObjectDecl>();
1163 auto thisType = getPointerBase( thisParam->get_type() );
1164 auto structType = dynamic_cast< const ast::StructInstType * >( thisType );
1165 if ( structType ) {
1166 structDecl = structType->base;
1167 for ( auto & member : structDecl->members ) {
1168 if ( auto field = member.as<ast::ObjectDecl>() ) {
1169 // record all of the struct type's members that need to be constructed or
1170 // destructed by the end of the function
1171 unhandled.insert( field );
1172 }
1173 }
1174 }
1175 }
1176 }
1177
1178 const ast::DeclWithType * GenStructMemberCalls::postvisit( const ast::FunctionDecl * funcDecl ) {
1179 // remove the unhandled objects from usedUninit, because a call is inserted
1180 // to handle them - only objects that are later constructed are used uninitialized.
1181 std::map< const ast::DeclWithType *, CodeLocation > diff;
1182 // need the comparator since usedUninit and unhandled have different types
1183 struct comp_t {
1184 typedef decltype(usedUninit)::value_type usedUninit_t;
1185 typedef decltype(unhandled)::value_type unhandled_t;
1186 bool operator()(usedUninit_t x, unhandled_t y) { return x.first < y; }
1187 bool operator()(unhandled_t x, usedUninit_t y) { return x < y.first; }
1188 } comp;
1189 std::set_difference( usedUninit.begin(), usedUninit.end(), unhandled.begin(), unhandled.end(), std::inserter( diff, diff.begin() ), comp );
1190 for ( auto p : diff ) {
1191 auto member = p.first;
1192 auto loc = p.second;
1193 // xxx - make error message better by also tracking the location that the object is constructed at?
1194 emit( loc, "in ", function->name, ", field ", member->name, " used before being constructed" );
1195 }
1196
1197 const CodeLocation loc = funcDecl->location;
1198
1199 if ( ! unhandled.empty() ) {
1200 auto mutStmts = function->stmts.get_and_mutate();
1201 // need to explicitly re-add function parameters to the indexer in order to resolve copy constructors
1202 auto guard = makeFuncGuard( [this]() { symtab.enterScope(); }, [this]() { symtab.leaveScope(); } );
1203 symtab.addFunction( function );
1204 auto global = transUnit().global;
1205
1206 // need to iterate through members in reverse in order for
1207 // ctor/dtor statements to come out in the right order
1208 for ( auto & member : reverseIterate( structDecl->members ) ) {
1209 auto field = member.as<ast::ObjectDecl>();
1210 // skip non-DWT members
1211 if ( ! field ) continue;
1212 // skip non-constructable members
1213 if ( ! tryConstruct( field ) ) continue;
1214 // skip handled members
1215 if ( ! unhandled.count( field ) ) continue;
1216
1217 // insert and resolve default/copy constructor call for each field that's unhandled
1218 // std::list< const ast::Stmt * > stmt;
1219 ast::Expr * arg2 = nullptr;
1220 if ( function->name == "?{}" && isCopyFunction( function ) ) {
1221 // if copy ctor, need to pass second-param-of-this-function.field
1222 // std::list< DeclarationWithType * > & params = function->get_functionType()->get_parameters();
1223 assert( function->params.size() == 2 );
1224 arg2 = new ast::MemberExpr(funcDecl->location, field, new ast::VariableExpr(funcDecl->location, function->params.back() ) );
1225 }
1226 InitExpander_new srcParam( arg2 );
1227 // cast away reference type and construct field.
1228 ast::Expr * thisExpr = new ast::CastExpr(funcDecl->location, new ast::VariableExpr(funcDecl->location, thisParam ), thisParam->get_type()->stripReferences());
1229 ast::Expr * memberDest = new ast::MemberExpr(funcDecl->location, field, thisExpr );
1230 ast::ptr<ast::Stmt> callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );
1231
1232 if ( callStmt ) {
1233 // auto & callStmt = stmt.front();
1234
1235 try {
1236 callStmt = callStmt->accept( *visitor );
1237 if ( isCtor ) {
1238 mutStmts->push_front( callStmt );
1239 } else { // TODO: don't generate destructor function/object for intrinsic calls
1240 // destructor statements should be added at the end
1241 // function->get_statements()->push_back( callStmt );
1242
1243 // Optimization: do not need to call intrinsic destructors on members
1244 if ( isIntrinsicSingleArgCallStmt( callStmt ) ) continue;
1245
1246 // __Destructor _dtor0 = { (void *)&b.a1, (void (*)(void *)_destroy_A };
1247 std::list< ast::ptr<ast::Stmt> > stmtsToAdd;
1248
1249 static UniqueName memberDtorNamer = { "__memberDtor" };
1250 assertf( global.dtorStruct, "builtin __Destructor not found." );
1251 assertf( global.dtorDestroy, "builtin __destroy_Destructor not found." );
1252
1253 ast::Expr * thisExpr = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, thisParam ) ), new ast::PointerType( new ast::VoidType(), ast::CV::Qualifiers() ) );
1254 ast::Expr * dtorExpr = new ast::VariableExpr(loc, getDtorFunc( thisParam, callStmt, stmtsToAdd ) );
1255
1256 // cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
1257 auto dtorFtype = new ast::FunctionType();
1258 dtorFtype->params.emplace_back( new ast::PointerType( new ast::VoidType() ) );
1259 auto dtorType = new ast::PointerType( dtorFtype );
1260
1261 auto destructor = new ast::ObjectDecl(loc, memberDtorNamer.newName(), new ast::StructInstType( global.dtorStruct ), new ast::ListInit(loc, { new ast::SingleInit(loc, thisExpr ), new ast::SingleInit(loc, new ast::CastExpr( dtorExpr, dtorType ) ) } ) );
1262 destructor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr( loc, global.dtorDestroy ) } ) );
1263 mutStmts->push_front( new ast::DeclStmt(loc, destructor ) );
1264 mutStmts->kids.splice( mutStmts->kids.begin(), stmtsToAdd );
1265 }
1266 } catch ( SemanticErrorException & error ) {
1267 emit( funcDecl->location, "in ", function->name , ", field ", field->name, " not explicitly ", isCtor ? "constructed" : "destructed", " and no ", isCtor ? "default constructor" : "destructor", " found" );
1268 }
1269 }
1270 }
1271 function->stmts = mutStmts;
1272 }
1273 if (! errors.isEmpty()) {
1274 throw errors;
1275 }
1276 // return funcDecl;
1277 return function;
1278 }
1279
1280 /// true if expr is effectively just the 'this' parameter
1281 bool isThisExpression( const ast::Expr * expr, const ast::DeclWithType * thisParam ) {
1282 // TODO: there are more complicated ways to pass 'this' to a constructor, e.g. &*, *&, etc.
1283 if ( auto varExpr = dynamic_cast< const ast::VariableExpr * >( expr ) ) {
1284 return varExpr->var == thisParam;
1285 } else if ( auto castExpr = dynamic_cast< const ast::CastExpr * > ( expr ) ) {
1286 return isThisExpression( castExpr->arg, thisParam );
1287 }
1288 return false;
1289 }
1290
1291 /// returns a MemberExpr if expr is effectively just member access on the 'this' parameter, else nullptr
1292 const ast::MemberExpr * isThisMemberExpr( const ast::Expr * expr, const ast::DeclWithType * thisParam ) {
1293 if ( auto memberExpr = dynamic_cast< const ast::MemberExpr * >( expr ) ) {
1294 if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
1295 return memberExpr;
1296 }
1297 } else if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
1298 return isThisMemberExpr( castExpr->arg, thisParam );
1299 }
1300 return nullptr;
1301 }
1302
1303 void GenStructMemberCalls::previsit( const ast::ApplicationExpr * appExpr ) {
1304 if ( ! checkWarnings( function ) ) {
1305 visit_children = false;
1306 return;
1307 }
1308
1309 std::string fname = getFunctionName( appExpr );
1310 if ( fname == function->name ) {
1311 // call to same kind of function
1312 const ast::Expr * firstParam = appExpr->args.front();
1313
1314 if ( isThisExpression( firstParam, thisParam ) ) {
1315 // if calling another constructor on thisParam, assume that function handles
1316 // all members - if it doesn't a warning will appear in that function.
1317 unhandled.clear();
1318 } else if ( auto memberExpr = isThisMemberExpr( firstParam, thisParam ) ) {
1319 // if first parameter is a member expression on the this parameter,
1320 // then remove the member from unhandled set.
1321 if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
1322 unhandled.erase( memberExpr->member );
1323 }
1324 }
1325 }
1326 }
1327
1328 void GenStructMemberCalls::previsit( const ast::MemberExpr * memberExpr ) {
1329 if ( ! checkWarnings( function ) || ! isCtor ) {
1330 visit_children = false;
1331 return;
1332 }
1333
1334 if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
1335 if ( unhandled.count( memberExpr->member ) ) {
1336 // emit a warning because a member was used before it was constructed
1337 usedUninit.insert( { memberExpr->member, memberExpr->location } );
1338 }
1339 }
1340 }
1341
1342 template< typename... Params >
1343 void GenStructMemberCalls::emit( CodeLocation loc, const Params &... params ) {
1344 SemanticErrorException err( loc, toString( params... ) );
1345 errors.append( err );
1346 }
1347
1348 const ast::Expr * GenStructMemberCalls::postvisit( const ast::UntypedExpr * untypedExpr ) {
1349 // xxx - functions returning ast::ptr seems wrong...
1350 auto res = ResolvExpr::findVoidExpression( untypedExpr, { symtab, transUnit().global } );
1351 return res.release();
1352 }
1353
1354 void InsertImplicitCalls::previsit(const ast::UniqueExpr * unqExpr) {
1355 if (visitedIds.count(unqExpr->id)) visit_children = false;
1356 else visitedIds.insert(unqExpr->id);
1357 }
1358
1359 const ast::Expr * FixCtorExprs::postvisit( const ast::ConstructorExpr * ctorExpr ) {
1360 const CodeLocation loc = ctorExpr->location;
1361 static UniqueName tempNamer( "_tmp_ctor_expr" );
1362 // xxx - is the size check necessary?
1363 assert( ctorExpr->result && ctorExpr->result->size() == 1 );
1364
1365 // xxx - this can be TupleAssignExpr now. Need to properly handle this case.
1366 // take possession of expr and env
1367 ast::ptr<ast::ApplicationExpr> callExpr = ctorExpr->callExpr.strict_as<ast::ApplicationExpr>();
1368 ast::ptr<ast::TypeSubstitution> env = ctorExpr->env;
1369 // ctorExpr->set_callExpr( nullptr );
1370 // ctorExpr->set_env( nullptr );
1371
1372 // xxx - ideally we would reuse the temporary generated from the copy constructor passes from within firstArg if it exists and not generate a temporary if it's unnecessary.
1373 auto tmp = new ast::ObjectDecl(loc, tempNamer.newName(), callExpr->args.front()->result );
1374 declsToAddBefore.push_back( tmp );
1375
1376 // build assignment and replace constructor's first argument with new temporary
1377 auto mutCallExpr = callExpr.get_and_mutate();
1378 const ast::Expr * firstArg = callExpr->args.front();
1379 ast::Expr * assign = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ), { new ast::AddressExpr(loc, new ast::VariableExpr(loc, tmp ) ), new ast::AddressExpr( firstArg ) } );
1380 firstArg = new ast::VariableExpr(loc, tmp );
1381 mutCallExpr->args.front() = firstArg;
1382
1383 // resolve assignment and dispose of new env
1384 auto resolved = ResolvExpr::findVoidExpression( assign, { symtab, transUnit().global } );
1385 auto mut = resolved.get_and_mutate();
1386 assertf(resolved.get() == mut, "newly resolved expression must be unique");
1387 mut->env = nullptr;
1388
1389 // for constructor expr:
1390 // T x;
1391 // x{};
1392 // results in:
1393 // T x;
1394 // T & tmp;
1395 // &tmp = &x, ?{}(tmp), tmp
1396 ast::CommaExpr * commaExpr = new ast::CommaExpr(loc, resolved, new ast::CommaExpr(loc, mutCallExpr, new ast::VariableExpr(loc, tmp ) ) );
1397 commaExpr->env = env;
1398 return commaExpr;
1399 }
1400} // namespace
1401} // namespace InitTweak
1402
1403// Local Variables: //
1404// tab-width: 4 //
1405// mode: c++ //
1406// compile-command: "make install" //
1407// End: //
Note: See TracBrowser for help on using the repository browser.