Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/InitTweak/FixInitNew.cpp

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