source: src/InitTweak/FixInit.cpp @ df78cce

Last change on this file since df78cce was 4e2f1b2, checked in by Andrew Beach <ajbeach@…>, 9 months ago

Clean-up of GenImplicitCall? module. Changing the return type for consistency spilled out into some other files, but that should also saves some operations. The other big one is the template instances were reduced to one and then the templates removed.

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