Changeset 2a301ff for src/InitTweak/FixInitNew.cpp
- Timestamp:
- Aug 31, 2023, 11:31:15 PM (2 years ago)
- Branches:
- master
- Children:
- 950c58e
- Parents:
- 92355883 (diff), 686912c (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/InitTweak/FixInitNew.cpp
r92355883 r2a301ff 22 22 #include "AST/SymbolTable.hpp" 23 23 #include "AST/Type.hpp" 24 #include "CodeGen/GenType.h" // for genPrettyType 25 #include "CodeGen/OperatorTable.h" 26 #include "Common/PassVisitor.h" // for PassVisitor, WithStmtsToAdd 24 #include "CodeGen/OperatorTable.h" // for isConstructor, isCtorDtor, isD... 27 25 #include "Common/SemanticError.h" // for SemanticError 28 26 #include "Common/ToString.hpp" // for toCString … … 33 31 #include "ResolvExpr/Resolver.h" // for findVoidExpression 34 32 #include "ResolvExpr/Unify.h" // for typesCompatible 35 #include "SymTab/Autogen.h" // for genImplicitCall36 33 #include "SymTab/GenImplicitCall.hpp" // for genImplicitCall 37 #include "SymTab/Indexer.h" // for Indexer38 #include "SymTab/Mangler.h" // for Mangler39 #include "SynTree/LinkageSpec.h" // for C, Spec, Cforall, isBuiltin40 #include "SynTree/Attribute.h" // for Attribute41 #include "SynTree/Constant.h" // for Constant42 #include "SynTree/Declaration.h" // for ObjectDecl, FunctionDecl, Decl...43 #include "SynTree/Expression.h" // for UniqueExpr, VariableExpr, Unty...44 #include "SynTree/Initializer.h" // for ConstructorInit, SingleInit45 #include "SynTree/Label.h" // for Label, operator<46 #include "SynTree/Mutator.h" // for mutateAll, Mutator, maybeMutate47 #include "SynTree/Statement.h" // for ExprStmt, CompoundStmt, Branch...48 #include "SynTree/Type.h" // for Type, Type::StorageClasses49 #include "SynTree/TypeSubstitution.h" // for TypeSubstitution, operator<<50 #include "SynTree/DeclReplacer.h" // for DeclReplacer51 #include "SynTree/Visitor.h" // for acceptAll, maybeAccept52 #include "Validate/FindSpecialDecls.h" // for dtorStmt, dtorStructDestroy53 34 54 35 extern bool ctordtorp; // print all debug … … 61 42 62 43 namespace InitTweak { 44 63 45 namespace { 64 46 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 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 90 741 ); 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 }; 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 263 1350 } // namespace 264 1351 … … 293 1380 } 294 1381 295 namespace {296 /// find and return the destructor used in `input`. If `input` is not a simple destructor call, generate a thunk297 /// that wraps the destructor, insert it into `stmtsToAdd` and return the new function declaration298 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 wrapper301 // 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 expression308 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 simply316 // 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 that324 // 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 reference338 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-level352 // 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 null357 *i = (*i)->accept(fixer);358 translationUnit.decls.splice( i, fixer.core.staticDtorDecls );359 } catch( SemanticErrorException &e ) {360 errors.append( e );361 } // try362 } // for363 if ( ! errors.isEmpty() ) {364 throw errors;365 } // if366 }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 back370 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 destroyed384 // in the correct places385 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 expressions391 visit_children = false;392 }393 394 // Relatively simple structural comparison for expressions, needed to determine395 // 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 qualify409 result = false;410 }411 412 // ignore casts413 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() == 2455 // 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 functions466 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 constructor477 return appExpr;478 } // if479 } else if ( CodeGen::isDestructor( funcDecl->name ) ) {480 // correctness: never copy construct arguments to a destructor481 return appExpr;482 } // if483 } // if484 } // if485 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 constructed488 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 substitution494 // is needed to obtain the type of temporary variables so that copy495 // 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 ExprStmt531 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 expr535 536 // resolve copy constructor537 // should only be one alternative for copy ctor and dtor expressions, since all arguments are fixed538 // (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 } // if550 if ( auto assign = resolved.as<ast::TupleAssignExpr>() ) {551 // fix newly generated StmtExpr552 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 types566 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 constructor585 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, so590 // don't create the temporary and don't call the copy constructor591 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 function595 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 necessary603 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 temporary608 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 places618 // check for existing cleanup attribute before adding another(?)619 // need to add __Destructor for _tmp_cp variables as well620 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 destructor628 auto res = makeCtorDtor( "^?{}", ret );629 auto dtor = mutate(res);630 631 // if the chosen destructor is intrinsic, elide the generated dtor handler632 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 warnings645 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 arguments679 // DeclarationWithType * param = *iter++;680 formal = *iter++;681 }682 683 arg = copyConstructArg( arg, impCpCtorExpr, formal );684 } // for685 686 // each return value from the call needs to be connected with an ObjectDecl at the call site, which is687 // initialized with the return value and is destructed later688 // 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 } // for701 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 much707 708 // xxx - actual env might be somewhere else, need to keep invariant709 710 // deletion of wrapper should be handled by pass template now711 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 temporary723 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 retExpr729 // std::swap( assign->env, appExpr->env );730 assign->env = appExpr->env;731 // actual env is handled by common routine that replaces WithTypeSubstitution732 return postvisit((const ast::Expr *)assign);733 } else {734 return postvisit((const ast::Expr *)appExpr);735 } // if736 }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(); // decl742 // g([A] x, [A] y); // decl743 // g(f()); // call744 // 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 added746 // to the outer context, rather than inside of the statement expression.747 748 // call the common routine that replaces WithTypeSubstitution749 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 statements758 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 } // for765 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 expr783 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_line791 );792 793 const ast::ExprStmt * last = stmtExpr->resultExpr;794 // xxx - if this is non-unique, need to copy while making resultExpr ref795 assertf(last->unique(), "attempt to modify weakly shared statement");796 auto mutLast = mutate(last);797 // above assertion means in-place mutation is OK798 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 statement810 stmtsToAddAfter.push_back( new ast::ExprStmt(loc, makeCtorDtor( "^?{}", ret ) ) );811 812 // must have a non-empty body, otherwise it wouldn't have a result813 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 returns816 stmts.push_back( new ast::ExprStmt(loc, new ast::VariableExpr(loc, ret ) ) );817 } // if818 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 empty830 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 empty835 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 generated848 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 its852 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 rethought858 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 call863 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 expression866 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 consistency873 // 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-NULL893 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, but898 // we get memory errors with this approach. To remedy this, the static899 // 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 = true920 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 if931 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 together937 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 reassigned944 // 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 to949 // hoist it and the object into the global space and950 // 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 scope966 staticDtorDecls.push_back( objDecl );967 staticDtorDecls.push_back( dtorCaller );968 969 // need to rename object uniquely since it now appears970 // at global scope and there could be multiple function-scoped971 // static variables with the same name in different functions.972 // Note: it isn't sufficient to modify only the mangleName, because973 // then subsequent Indexer passes can choke on seeing the object's name974 // if another object has the same name and type. An unfortunate side-effect975 // 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 scope983 // create a new object which is never used984 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 SingleInits998 const ast::Expr * ctorArg = ctorCall->args.back();999 // ctorCall should be gone afterwards1000 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, but1018 // non-intrinsic dtors must be called1019 if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {1020 // set dtor location to the object's location for error messages1021 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 } // if1025 }1026 } // if1027 } 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 okay1032 objDecl->init = nullptr;1033 } // if1034 // delete ctorInit;1035 return objDecl;1036 } // if1037 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 scope1046 if ( auto objDecl = stmt->decl.as<ast::ObjectDecl>() ) {1047 curVars.push_back( objDecl );1048 } // if1049 }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 } // for1056 }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 labels1071 GuardValue( labelVars );1072 labelVars.clear();1073 // LabelFinder does not recurse into FunctionDecl, so need to visit1074 // 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 the1083 // BranchStmt but not at the labelled (target) statement must be destructed. If there are any objects in scope1084 // at the target location but not at the BranchStmt then those objects would be uninitialized so notify the user1085 // 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 goto1088 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 definition1092 // S_G = curVars = set of objects in scope at goto statement1093 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-empty1108 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 } // if1115 }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 should1122 // 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 } // switch1129 }1130 1131 bool checkWarnings( const ast::FunctionDecl * funcDecl ) {1132 // only check for warnings if the current function is a user-defined1133 // constructor or destructor1134 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 errors1147 1148 // need to start with fresh sets1149 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 or1170 // destructed by the end of the function1171 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 inserted1180 // 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 types1183 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 constructors1202 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 for1207 // ctor/dtor statements to come out in the right order1208 for ( auto & member : reverseIterate( structDecl->members ) ) {1209 auto field = member.as<ast::ObjectDecl>();1210 // skip non-DWT members1211 if ( ! field ) continue;1212 // skip non-constructable members1213 if ( ! tryConstruct( field ) ) continue;1214 // skip handled members1215 if ( ! unhandled.count( field ) ) continue;1216 1217 // insert and resolve default/copy constructor call for each field that's unhandled1218 // 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.field1222 // 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 calls1240 // destructor statements should be added at the end1241 // function->get_statements()->push_back( callStmt );1242 1243 // Optimization: do not need to call intrinsic destructors on members1244 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 warnings1257 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' parameter1281 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 nullptr1292 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 function1312 const ast::Expr * firstParam = appExpr->args.front();1313 1314 if ( isThisExpression( firstParam, thisParam ) ) {1315 // if calling another constructor on thisParam, assume that function handles1316 // 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 constructed1337 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 env1367 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 temporary1377 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 env1384 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), tmp1396 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 } // namespace1401 1382 } // namespace InitTweak 1402 1383
Note:
See TracChangeset
for help on using the changeset viewer.