Changeset 0e707bd


Ignore:
Timestamp:
Oct 30, 2020, 3:59:07 PM (12 months ago)
Author:
Andrew Beach <ajbeach@…>
Branches:
arm-eh, jacob/cs343-translation, master, new-ast-unique-expr
Children:
6a036eb
Parents:
36d0a80
Message:

Fixed indentation (no indent in whole file namespace) in FixInitNew?.cpp.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/InitTweak/FixInitNew.cpp

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