Ignore:
Timestamp:
Aug 31, 2023, 11:31:15 PM (2 years ago)
Author:
JiadaL <j82liang@…>
Branches:
master
Children:
950c58e
Parents:
92355883 (diff), 686912c (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:

Resolve conflict

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/InitTweak/FixInitNew.cpp

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