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