Changeset 4852232
- Timestamp:
- Jul 31, 2023, 4:18:59 PM (11 months ago)
- Branches:
- master
- Children:
- 210c737
- Parents:
- 000d68f (diff), 17c13b9 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/InitTweak/FixInitNew.cpp
r000d68f r4852232 42 42 43 43 namespace InitTweak { 44 44 45 namespace { 45 46 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 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 71 741 ); 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 }; 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 244 1350 } // namespace 245 1351 … … 274 1380 } 275 1381 276 namespace {277 /// find and return the destructor used in `input`. If `input` is not a simple destructor call, generate a thunk278 /// that wraps the destructor, insert it into `stmtsToAdd` and return the new function declaration279 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 wrapper282 // 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 expression289 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 simply297 // 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 that305 // 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 reference319 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-level333 // 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 null338 *i = (*i)->accept(fixer);339 translationUnit.decls.splice( i, fixer.core.staticDtorDecls );340 } catch( SemanticErrorException &e ) {341 errors.append( e );342 } // try343 } // for344 if ( ! errors.isEmpty() ) {345 throw errors;346 } // if347 }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 back351 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 destroyed365 // in the correct places366 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 expressions372 visit_children = false;373 }374 375 // Relatively simple structural comparison for expressions, needed to determine376 // 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 qualify390 result = false;391 }392 393 // ignore casts394 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() == 2436 // 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 functions447 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 constructor458 return appExpr;459 } // if460 } else if ( CodeGen::isDestructor( funcDecl->name ) ) {461 // correctness: never copy construct arguments to a destructor462 return appExpr;463 } // if464 } // if465 } // if466 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 constructed469 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 substitution475 // is needed to obtain the type of temporary variables so that copy476 // 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 ExprStmt512 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 expr516 517 // resolve copy constructor518 // should only be one alternative for copy ctor and dtor expressions, since all arguments are fixed519 // (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 } // if531 if ( auto assign = resolved.as<ast::TupleAssignExpr>() ) {532 // fix newly generated StmtExpr533 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 types547 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 constructor566 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, so571 // don't create the temporary and don't call the copy constructor572 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 function576 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 necessary584 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 temporary589 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 places599 // check for existing cleanup attribute before adding another(?)600 // need to add __Destructor for _tmp_cp variables as well601 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 destructor609 auto res = makeCtorDtor( "^?{}", ret );610 auto dtor = mutate(res);611 612 // if the chosen destructor is intrinsic, elide the generated dtor handler613 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 warnings626 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 arguments660 // DeclarationWithType * param = *iter++;661 formal = *iter++;662 }663 664 arg = copyConstructArg( arg, impCpCtorExpr, formal );665 } // for666 667 // each return value from the call needs to be connected with an ObjectDecl at the call site, which is668 // initialized with the return value and is destructed later669 // 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 } // for682 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 much688 689 // xxx - actual env might be somewhere else, need to keep invariant690 691 // deletion of wrapper should be handled by pass template now692 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 temporary704 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 retExpr710 // std::swap( assign->env, appExpr->env );711 assign->env = appExpr->env;712 // actual env is handled by common routine that replaces WithTypeSubstitution713 return postvisit((const ast::Expr *)assign);714 } else {715 return postvisit((const ast::Expr *)appExpr);716 } // if717 }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(); // decl723 // g([A] x, [A] y); // decl724 // g(f()); // call725 // 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 added727 // to the outer context, rather than inside of the statement expression.728 729 // call the common routine that replaces WithTypeSubstitution730 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 statements739 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 } // for746 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 expr764 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_line772 );773 774 const ast::ExprStmt * last = stmtExpr->resultExpr;775 // xxx - if this is non-unique, need to copy while making resultExpr ref776 assertf(last->unique(), "attempt to modify weakly shared statement");777 auto mutLast = mutate(last);778 // above assertion means in-place mutation is OK779 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 statement791 stmtsToAddAfter.push_back( new ast::ExprStmt(loc, makeCtorDtor( "^?{}", ret ) ) );792 793 // must have a non-empty body, otherwise it wouldn't have a result794 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 returns797 stmts.push_back( new ast::ExprStmt(loc, new ast::VariableExpr(loc, ret ) ) );798 } // if799 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 empty811 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 empty816 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 generated829 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 its833 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 rethought839 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 call844 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 expression847 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 consistency854 // 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-NULL874 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, but879 // we get memory errors with this approach. To remedy this, the static880 // 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 = true901 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 if912 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 together918 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 reassigned925 // 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 to930 // hoist it and the object into the global space and931 // 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 scope947 staticDtorDecls.push_back( objDecl );948 staticDtorDecls.push_back( dtorCaller );949 950 // need to rename object uniquely since it now appears951 // at global scope and there could be multiple function-scoped952 // static variables with the same name in different functions.953 // Note: it isn't sufficient to modify only the mangleName, because954 // then subsequent Indexer passes can choke on seeing the object's name955 // if another object has the same name and type. An unfortunate side-effect956 // 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 scope964 // create a new object which is never used965 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 SingleInits979 const ast::Expr * ctorArg = ctorCall->args.back();980 // ctorCall should be gone afterwards981 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, but999 // non-intrinsic dtors must be called1000 if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {1001 // set dtor location to the object's location for error messages1002 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 } // if1006 }1007 } // if1008 } 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 okay1013 objDecl->init = nullptr;1014 } // if1015 // delete ctorInit;1016 return objDecl;1017 } // if1018 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 scope1027 if ( auto objDecl = stmt->decl.as<ast::ObjectDecl>() ) {1028 curVars.push_back( objDecl );1029 } // if1030 }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 } // for1037 }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 labels1052 GuardValue( labelVars );1053 labelVars.clear();1054 // LabelFinder does not recurse into FunctionDecl, so need to visit1055 // 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 the1064 // BranchStmt but not at the labelled (target) statement must be destructed. If there are any objects in scope1065 // at the target location but not at the BranchStmt then those objects would be uninitialized so notify the user1066 // 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 goto1069 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 definition1073 // S_G = curVars = set of objects in scope at goto statement1074 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-empty1089 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 } // if1096 }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 should1103 // 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 } // switch1110 }1111 1112 bool checkWarnings( const ast::FunctionDecl * funcDecl ) {1113 // only check for warnings if the current function is a user-defined1114 // constructor or destructor1115 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 errors1128 1129 // need to start with fresh sets1130 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 or1151 // destructed by the end of the function1152 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 inserted1161 // 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 types1164 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 constructors1183 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 for1188 // ctor/dtor statements to come out in the right order1189 for ( auto & member : reverseIterate( structDecl->members ) ) {1190 auto field = member.as<ast::ObjectDecl>();1191 // skip non-DWT members1192 if ( ! field ) continue;1193 // skip non-constructable members1194 if ( ! tryConstruct( field ) ) continue;1195 // skip handled members1196 if ( ! unhandled.count( field ) ) continue;1197 1198 // insert and resolve default/copy constructor call for each field that's unhandled1199 // 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.field1203 // 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 calls1221 // destructor statements should be added at the end1222 // function->get_statements()->push_back( callStmt );1223 1224 // Optimization: do not need to call intrinsic destructors on members1225 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 warnings1238 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' parameter1262 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 nullptr1273 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 function1293 const ast::Expr * firstParam = appExpr->args.front();1294 1295 if ( isThisExpression( firstParam, thisParam ) ) {1296 // if calling another constructor on thisParam, assume that function handles1297 // 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 constructed1318 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 env1348 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 temporary1358 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 env1365 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), tmp1377 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 } // namespace1382 1382 } // namespace InitTweak 1383 1383
Note: See TracChangeset
for help on using the changeset viewer.