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