source: src/InitTweak/FixInitNew.cpp @ 0658672

ast-experimental
Last change on this file since 0658672 was fb4dc28, checked in by Andrew Beach <ajbeach@…>, 19 months ago

Moved new ast code out of one of the old files. The new file may have to change if SymTab? is removed entirely, but for now at least, there is a lot less template code in headers.

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