source: src/InitTweak/FixInit.cpp@ f5212ca

Last change on this file since f5212ca was 4e2f1b2, checked in by Andrew Beach <ajbeach@…>, 19 months ago

Clean-up of GenImplicitCall module. Changing the return type for consistency spilled out into some other files, but that should also saves some operations. The other big one is the template instances were reduced to one and then the templates removed.

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