Changeset 4852232


Ignore:
Timestamp:
Jul 31, 2023, 4:18:59 PM (11 months ago)
Author:
caparsons <caparson@…>
Branches:
master
Children:
210c737
Parents:
000d68f (diff), 17c13b9 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/InitTweak/FixInitNew.cpp

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