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