Changes in / [4852232:000d68f]


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/InitTweak/FixInitNew.cpp

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