Changeset f53acdf8 for src/ResolvExpr/Resolver.cc
- Timestamp:
- Jul 19, 2019, 2:16:01 PM (5 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 4eb43fa
- Parents:
- 1f1c102 (diff), 8ac3b0e (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/ResolvExpr/Resolver.cc
r1f1c102 rf53acdf8 562 562 // TODO: Replace *exception type with &exception type. 563 563 if ( throwStmt->get_expr() ) { 564 StructDecl * exception_decl = 565 indexer.lookupStruct( "__cfaabi_ehm__base_exception_t" ); 564 const StructDecl * exception_decl = indexer.lookupStruct( "__cfaabi_ehm__base_exception_t" ); 566 565 assert( exception_decl ); 567 Type * exceptType = new PointerType( noQualifiers, new StructInstType( noQualifiers, exception_decl) );566 Type * exceptType = new PointerType( noQualifiers, new StructInstType( noQualifiers, const_cast<StructDecl *>(exception_decl) ) ); 568 567 findSingleExpression( throwStmt->expr, exceptType, indexer ); 569 568 } … … 972 971 /// Calls the CandidateFinder and finds the single best candidate 973 972 CandidateRef findUnfinishedKindExpression( 974 const ast::Expr * untyped, const ast::SymbolTable & symtab, const std::string & kind, 973 const ast::Expr * untyped, const ast::SymbolTable & symtab, const std::string & kind, 975 974 std::function<bool(const Candidate &)> pred = anyCandidate, ResolvMode mode = {} 976 975 ) { … … 994 993 // produce invalid error if no candidates 995 994 if ( candidates.empty() ) { 996 SemanticError( untyped, 997 toString( "No reasonable alternatives for ", kind, (kind != "" ? " " : ""), 995 SemanticError( untyped, 996 toString( "No reasonable alternatives for ", kind, (kind != "" ? " " : ""), 998 997 "expression: ") ); 999 998 } … … 1031 1030 if ( winners.size() != 1 ) { 1032 1031 std::ostringstream stream; 1033 stream << "Cannot choose between " << winners.size() << " alternatives for " 1032 stream << "Cannot choose between " << winners.size() << " alternatives for " 1034 1033 << kind << (kind != "" ? " " : "") << "expression\n"; 1035 1034 ast::print( stream, untyped ); … … 1054 1053 struct StripCasts_new final { 1055 1054 const ast::Expr * postmutate( const ast::CastExpr * castExpr ) { 1056 if ( 1057 castExpr->isGenerated 1058 && typesCompatible( castExpr->arg->result, castExpr->result ) 1055 if ( 1056 castExpr->isGenerated 1057 && typesCompatible( castExpr->arg->result, castExpr->result ) 1059 1058 ) { 1060 1059 // generated cast is the same type as its argument, remove it after keeping env 1061 return ast::mutate_field( 1060 return ast::mutate_field( 1062 1061 castExpr->arg.get(), &ast::Expr::env, castExpr->env ); 1063 1062 } … … 1088 1087 1089 1088 /// Establish post-resolver invariants for expressions 1090 void finishExpr( 1091 ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env, 1089 void finishExpr( 1090 ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env, 1092 1091 const ast::TypeSubstitution * oldenv = nullptr 1093 1092 ) { 1094 1093 // set up new type substitution for expression 1095 ast::ptr< ast::TypeSubstitution > newenv = 1094 ast::ptr< ast::TypeSubstitution > newenv = 1096 1095 oldenv ? oldenv : new ast::TypeSubstitution{}; 1097 1096 env.writeToSubstitution( *newenv.get_and_mutate() ); … … 1102 1101 } // anonymous namespace 1103 1102 1104 1103 1105 1104 ast::ptr< ast::Expr > resolveInVoidContext( 1106 1105 const ast::Expr * expr, const ast::SymbolTable & symtab, ast::TypeEnvironment & env 1107 1106 ) { 1108 1107 assertf( expr, "expected a non-null expression" ); 1109 1108 1110 1109 // set up and resolve expression cast to void 1111 1110 ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr }; 1112 CandidateRef choice = findUnfinishedKindExpression( 1111 CandidateRef choice = findUnfinishedKindExpression( 1113 1112 untyped, symtab, "", anyCandidate, ResolvMode::withAdjustment() ); 1114 1113 1115 1114 // a cast expression has either 0 or 1 interpretations (by language rules); 1116 1115 // if 0, an exception has already been thrown, and this code will not run … … 1122 1121 1123 1122 namespace { 1124 /// Resolve `untyped` to the expression whose candidate is the best match for a `void` 1123 /// Resolve `untyped` to the expression whose candidate is the best match for a `void` 1125 1124 /// context. 1126 ast::ptr< ast::Expr > findVoidExpression( 1125 ast::ptr< ast::Expr > findVoidExpression( 1127 1126 const ast::Expr * untyped, const ast::SymbolTable & symtab 1128 1127 ) { … … 1134 1133 } 1135 1134 1136 /// resolve `untyped` to the expression whose candidate satisfies `pred` with the 1135 /// resolve `untyped` to the expression whose candidate satisfies `pred` with the 1137 1136 /// lowest cost, returning the resolved version 1138 1137 ast::ptr< ast::Expr > findKindExpression( 1139 const ast::Expr * untyped, const ast::SymbolTable & symtab, 1140 std::function<bool(const Candidate &)> pred = anyCandidate, 1138 const ast::Expr * untyped, const ast::SymbolTable & symtab, 1139 std::function<bool(const Candidate &)> pred = anyCandidate, 1141 1140 const std::string & kind = "", ResolvMode mode = {} 1142 1141 ) { 1143 1142 if ( ! untyped ) return {}; 1144 CandidateRef choice = 1143 CandidateRef choice = 1145 1144 findUnfinishedKindExpression( untyped, symtab, kind, pred, mode ); 1146 1145 finishExpr( choice->expr, choice->env, untyped->env ); … … 1149 1148 1150 1149 /// Resolve `untyped` to the single expression whose candidate is the best match 1151 ast::ptr< ast::Expr > findSingleExpression( 1152 const ast::Expr * untyped, const ast::SymbolTable & symtab 1150 ast::ptr< ast::Expr > findSingleExpression( 1151 const ast::Expr * untyped, const ast::SymbolTable & symtab 1153 1152 ) { 1154 1153 return findKindExpression( untyped, symtab ); … … 1170 1169 bool hasIntegralType( const Candidate & i ) { 1171 1170 const ast::Type * type = i.expr->result; 1172 1171 1173 1172 if ( auto bt = dynamic_cast< const ast::BasicType * >( type ) ) { 1174 1173 return bt->isInteger(); 1175 } else if ( 1176 dynamic_cast< const ast::EnumInstType * >( type ) 1174 } else if ( 1175 dynamic_cast< const ast::EnumInstType * >( type ) 1177 1176 || dynamic_cast< const ast::ZeroType * >( type ) 1178 1177 || dynamic_cast< const ast::OneType * >( type ) … … 1183 1182 1184 1183 /// Resolve `untyped` as an integral expression, returning the resolved version 1185 ast::ptr< ast::Expr > findIntegralExpression( 1186 const ast::Expr * untyped, const ast::SymbolTable & symtab 1184 ast::ptr< ast::Expr > findIntegralExpression( 1185 const ast::Expr * untyped, const ast::SymbolTable & symtab 1187 1186 ) { 1188 1187 return findKindExpression( untyped, symtab, hasIntegralType, "condition" ); … … 1192 1191 bool isCharType( const ast::Type * t ) { 1193 1192 if ( auto bt = dynamic_cast< const ast::BasicType * >( t ) ) { 1194 return bt->kind == ast::BasicType::Char 1195 || bt->kind == ast::BasicType::SignedChar 1193 return bt->kind == ast::BasicType::Char 1194 || bt->kind == ast::BasicType::SignedChar 1196 1195 || bt->kind == ast::BasicType::UnsignedChar; 1197 1196 } … … 1253 1252 } 1254 1253 1255 ast::ptr< ast::Init > resolveCtorInit( 1256 const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab 1254 ast::ptr< ast::Init > resolveCtorInit( 1255 const ast::ConstructorInit * ctorInit, const ast::SymbolTable & symtab 1257 1256 ) { 1258 1257 assert( ctorInit ); … … 1261 1260 } 1262 1261 1263 ast::ptr< ast::Expr > resolveStmtExpr( 1264 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab 1262 ast::ptr< ast::Expr > resolveStmtExpr( 1263 const ast::StmtExpr * stmtExpr, const ast::SymbolTable & symtab 1265 1264 ) { 1266 1265 assert( stmtExpr ); … … 1303 1302 1304 1303 void Resolver_new::previsit( const ast::ObjectDecl * objectDecl ) { 1305 // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()], 1306 // class-variable `initContext` is changed multiple times because the LHS is analyzed 1307 // twice. The second analysis changes `initContext` because a function type can contain 1308 // object declarations in the return and parameter types. Therefore each value of 1309 // `initContext` is retained so the type on the first analysis is preserved and used for 1304 // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()], 1305 // class-variable `initContext` is changed multiple times because the LHS is analyzed 1306 // twice. The second analysis changes `initContext` because a function type can contain 1307 // object declarations in the return and parameter types. Therefore each value of 1308 // `initContext` is retained so the type on the first analysis is preserved and used for 1310 1309 // selecting the RHS. 1311 1310 GuardValue( currentObject ); 1312 1311 currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() }; 1313 1312 if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) { 1314 // enumerator initializers should not use the enum type to initialize, since the 1313 // enumerator initializers should not use the enum type to initialize, since the 1315 1314 // enum type is still incomplete at this point. Use `int` instead. 1316 currentObject = ast::CurrentObject{ 1315 currentObject = ast::CurrentObject{ 1317 1316 objectDecl->location, new ast::BasicType{ ast::BasicType::SignedInt } }; 1318 1317 } … … 1325 1324 } 1326 1325 1327 const ast::StaticAssertDecl * Resolver_new::previsit( 1328 const ast::StaticAssertDecl * assertDecl 1326 const ast::StaticAssertDecl * Resolver_new::previsit( 1327 const ast::StaticAssertDecl * assertDecl 1329 1328 ) { 1330 return ast::mutate_field( 1331 assertDecl, &ast::StaticAssertDecl::cond, 1329 return ast::mutate_field( 1330 assertDecl, &ast::StaticAssertDecl::cond, 1332 1331 findIntegralExpression( assertDecl->cond, symtab ) ); 1333 1332 } … … 1338 1337 #warning should use new equivalent to Validate::SizeType rather than sizeType here 1339 1338 ast::ptr< ast::Type > sizeType = new ast::BasicType{ ast::BasicType::LongUnsignedInt }; 1340 ast::mutate_field( 1341 type, &PtrType::dimension, 1339 ast::mutate_field( 1340 type, &PtrType::dimension, 1342 1341 findSingleExpression( type->dimension, sizeType, symtab ) ); 1343 1342 } … … 1356 1355 visit_children = false; 1357 1356 assertf( exprStmt->expr, "ExprStmt has null expression in resolver" ); 1358 1359 return ast::mutate_field( 1357 1358 return ast::mutate_field( 1360 1359 exprStmt, &ast::ExprStmt::expr, findVoidExpression( exprStmt->expr, symtab ) ); 1361 1360 } … … 1364 1363 visit_children = false; 1365 1364 1366 asmExpr = ast::mutate_field( 1365 asmExpr = ast::mutate_field( 1367 1366 asmExpr, &ast::AsmExpr::operand, findVoidExpression( asmExpr->operand, symtab ) ); 1368 1367 1369 1368 if ( asmExpr->inout ) { 1370 1369 asmExpr = ast::mutate_field( 1371 1370 asmExpr, &ast::AsmExpr::inout, findVoidExpression( asmExpr->inout, symtab ) ); 1372 1371 } 1373 1372 1374 1373 return asmExpr; 1375 1374 } … … 1388 1387 1389 1388 const ast::WhileStmt * Resolver_new::previsit( const ast::WhileStmt * whileStmt ) { 1390 return ast::mutate_field( 1389 return ast::mutate_field( 1391 1390 whileStmt, &ast::WhileStmt::cond, findIntegralExpression( whileStmt->cond, symtab ) ); 1392 1391 } … … 1409 1408 GuardValue( currentObject ); 1410 1409 switchStmt = ast::mutate_field( 1411 switchStmt, &ast::SwitchStmt::cond, 1410 switchStmt, &ast::SwitchStmt::cond, 1412 1411 findIntegralExpression( switchStmt->cond, symtab ) ); 1413 1412 currentObject = ast::CurrentObject{ switchStmt->location, switchStmt->cond->result }; … … 1420 1419 assertf( initAlts.size() == 1, "SwitchStmt did not correctly resolve an integral " 1421 1420 "expression." ); 1422 1423 ast::ptr< ast::Expr > untyped = 1421 1422 ast::ptr< ast::Expr > untyped = 1424 1423 new ast::CastExpr{ caseStmt->location, caseStmt->cond, initAlts.front().type }; 1425 1424 ast::ptr< ast::Expr > newExpr = findSingleExpression( untyped, symtab ); 1426 1427 // case condition cannot have a cast in C, so it must be removed here, regardless of 1425 1426 // case condition cannot have a cast in C, so it must be removed here, regardless of 1428 1427 // whether it would perform a conversion. 1429 1428 if ( const ast::CastExpr * castExpr = newExpr.as< ast::CastExpr >() ) { 1430 1429 swap_and_save_env( newExpr, castExpr->arg ); 1431 1430 } 1432 1431 1433 1432 caseStmt = ast::mutate_field( caseStmt, &ast::CaseStmt::cond, newExpr ); 1434 1433 } … … 1443 1442 ast::ptr< ast::Type > target = new ast::PointerType{ new ast::VoidType{} }; 1444 1443 branchStmt = ast::mutate_field( 1445 branchStmt, &ast::BranchStmt::computedTarget, 1444 branchStmt, &ast::BranchStmt::computedTarget, 1446 1445 findSingleExpression( branchStmt->computedTarget, target, symtab ) ); 1447 1446 } … … 1453 1452 if ( returnStmt->expr ) { 1454 1453 returnStmt = ast::mutate_field( 1455 returnStmt, &ast::ReturnStmt::expr, 1454 returnStmt, &ast::ReturnStmt::expr, 1456 1455 findSingleExpression( returnStmt->expr, functionReturn, symtab ) ); 1457 1456 } … … 1462 1461 visit_children = false; 1463 1462 if ( throwStmt->expr ) { 1464 const ast::StructDecl * exceptionDecl = 1463 const ast::StructDecl * exceptionDecl = 1465 1464 symtab.lookupStruct( "__cfaabi_ehm__base_exception_t" ); 1466 1465 assert( exceptionDecl ); 1467 ast::ptr< ast::Type > exceptType = 1466 ast::ptr< ast::Type > exceptType = 1468 1467 new ast::PointerType{ new ast::StructInstType{ exceptionDecl } }; 1469 1468 throwStmt = ast::mutate_field( 1470 throwStmt, &ast::ThrowStmt::expr, 1469 throwStmt, &ast::ThrowStmt::expr, 1471 1470 findSingleExpression( throwStmt->expr, exceptType, symtab ) ); 1472 1471 } … … 1477 1476 if ( catchStmt->cond ) { 1478 1477 ast::ptr< ast::Type > boolType = new ast::BasicType{ ast::BasicType::Bool }; 1479 catchStmt = ast::mutate_field( 1480 catchStmt, &ast::CatchStmt::cond, 1478 catchStmt = ast::mutate_field( 1479 catchStmt, &ast::CatchStmt::cond, 1481 1480 findSingleExpression( catchStmt->cond, boolType, symtab ) ); 1482 1481 } … … 1506 1505 1507 1506 if ( clause.target.args.empty() ) { 1508 SemanticError( stmt->location, 1507 SemanticError( stmt->location, 1509 1508 "Waitfor clause must have at least one mutex parameter"); 1510 1509 } 1511 1510 1512 1511 // Find all alternatives for all arguments in canonical form 1513 std::vector< CandidateFinder > argFinders = 1512 std::vector< CandidateFinder > argFinders = 1514 1513 funcFinder.findSubExprs( clause.target.args ); 1515 1514 1516 1515 // List all combinations of arguments 1517 1516 std::vector< CandidateList > possibilities; … … 1519 1518 1520 1519 // For every possible function: 1521 // * try matching the arguments to the parameters, not the other way around because 1520 // * try matching the arguments to the parameters, not the other way around because 1522 1521 // more arguments than parameters 1523 1522 CandidateList funcCandidates; … … 1526 1525 for ( CandidateRef & func : funcFinder.candidates ) { 1527 1526 try { 1528 auto pointerType = dynamic_cast< const ast::PointerType * >( 1527 auto pointerType = dynamic_cast< const ast::PointerType * >( 1529 1528 func->expr->result->stripReferences() ); 1530 1529 if ( ! pointerType ) { 1531 SemanticError( stmt->location, func->expr->result.get(), 1530 SemanticError( stmt->location, func->expr->result.get(), 1532 1531 "candidate not viable: not a pointer type\n" ); 1533 1532 } … … 1535 1534 auto funcType = pointerType->base.as< ast::FunctionType >(); 1536 1535 if ( ! funcType ) { 1537 SemanticError( stmt->location, func->expr->result.get(), 1536 SemanticError( stmt->location, func->expr->result.get(), 1538 1537 "candidate not viable: not a function type\n" ); 1539 1538 } … … 1544 1543 1545 1544 if( ! nextMutex( param, paramEnd ) ) { 1546 SemanticError( stmt->location, funcType, 1545 SemanticError( stmt->location, funcType, 1547 1546 "candidate function not viable: no mutex parameters\n"); 1548 1547 } … … 1560 1559 ast::AssertionSet need, have; 1561 1560 ast::TypeEnvironment resultEnv{ func->env }; 1562 // Add all type variables as open so that those not used in the 1561 // Add all type variables as open so that those not used in the 1563 1562 // parameter list are still considered open 1564 1563 resultEnv.add( funcType->forall ); … … 1580 1579 unsigned n_mutex_param = 0; 1581 1580 1582 // For every argument of its set, check if it matches one of the 1581 // For every argument of its set, check if it matches one of the 1583 1582 // parameters. The order is important 1584 1583 for ( auto & arg : argsList ) { … … 1587 1586 // We ran out of parameters but still have arguments. 1588 1587 // This function doesn't match 1589 SemanticError( stmt->location, funcType, 1588 SemanticError( stmt->location, funcType, 1590 1589 toString("candidate function not viable: too many mutex " 1591 1590 "arguments, expected ", n_mutex_param, "\n" ) ); … … 1594 1593 ++n_mutex_param; 1595 1594 1596 // Check if the argument matches the parameter type in the current 1595 // Check if the argument matches the parameter type in the current 1597 1596 // scope 1598 1597 ast::ptr< ast::Type > paramType = (*param)->get_type(); 1599 if ( 1600 ! unify( 1601 arg->expr->result, paramType, resultEnv, need, have, open, 1602 symtab ) 1598 if ( 1599 ! unify( 1600 arg->expr->result, paramType, resultEnv, need, have, open, 1601 symtab ) 1603 1602 ) { 1604 1603 // Type doesn't match … … 1627 1626 } while ( nextMutex( param, paramEnd ) ); 1628 1627 1629 // We ran out of arguments but still have parameters left; this 1628 // We ran out of arguments but still have parameters left; this 1630 1629 // function doesn't match 1631 SemanticError( stmt->location, funcType, 1630 SemanticError( stmt->location, funcType, 1632 1631 toString( "candidate function not viable: too few mutex " 1633 1632 "arguments, expected ", n_mutex_param, "\n" ) ); … … 1657 1656 // Make sure correct number of arguments 1658 1657 if( funcCandidates.empty() ) { 1659 SemanticErrorException top( stmt->location, 1658 SemanticErrorException top( stmt->location, 1660 1659 "No alternatives for function in call to waitfor" ); 1661 1660 top.append( errors ); … … 1664 1663 1665 1664 if( argsCandidates.empty() ) { 1666 SemanticErrorException top( stmt->location, 1667 "No alternatives for arguments in call to waitfor" ); 1665 SemanticErrorException top( stmt->location, 1666 "No alternatives for arguments in call to waitfor" ); 1668 1667 top.append( errors ); 1669 1668 throw top; … … 1671 1670 1672 1671 if( funcCandidates.size() > 1 ) { 1673 SemanticErrorException top( stmt->location, 1672 SemanticErrorException top( stmt->location, 1674 1673 "Ambiguous function in call to waitfor" ); 1675 1674 top.append( errors ); … … 1686 1685 // build new clause 1687 1686 ast::WaitForStmt::Clause clause2; 1688 1687 1689 1688 clause2.target.func = funcCandidates.front()->expr; 1690 1689 1691 1690 clause2.target.args.reserve( clause.target.args.size() ); 1692 1691 for ( auto arg : argsCandidates.front() ) { … … 1708 1707 ast::WaitForStmt::Timeout timeout2; 1709 1708 1710 ast::ptr< ast::Type > target = 1709 ast::ptr< ast::Type > target = 1711 1710 new ast::BasicType{ ast::BasicType::LongLongUnsignedInt }; 1712 1711 timeout2.time = findSingleExpression( stmt->timeout.time, target, symtab ); … … 1740 1739 const ast::SingleInit * Resolver_new::previsit( const ast::SingleInit * singleInit ) { 1741 1740 visit_children = false; 1742 // resolve initialization using the possibilities as determined by the `currentObject` 1741 // resolve initialization using the possibilities as determined by the `currentObject` 1743 1742 // cursor. 1744 ast::ptr< ast::Expr > untyped = new ast::UntypedInitExpr{ 1743 ast::ptr< ast::Expr > untyped = new ast::UntypedInitExpr{ 1745 1744 singleInit->location, singleInit->value, currentObject.getOptions() }; 1746 1745 ast::ptr<ast::Expr> newExpr = findSingleExpression( untyped, symtab ); … … 1751 1750 1752 1751 // discard InitExpr wrapper and retain relevant pieces. 1753 // `initExpr` may have inferred params in the case where the expression specialized a 1754 // function pointer, and newExpr may already have inferParams of its own, so a simple 1752 // `initExpr` may have inferred params in the case where the expression specialized a 1753 // function pointer, and newExpr may already have inferParams of its own, so a simple 1755 1754 // swap is not sufficient 1756 1755 ast::Expr::InferUnion inferred = initExpr->inferred; … … 1758 1757 newExpr.get_and_mutate()->inferred.splice( std::move(inferred) ); 1759 1758 1760 // get the actual object's type (may not exactly match what comes back from the resolver 1759 // get the actual object's type (may not exactly match what comes back from the resolver 1761 1760 // due to conversions) 1762 1761 const ast::Type * initContext = currentObject.getCurrentType(); … … 1770 1769 if ( auto pt = newExpr->result.as< ast::PointerType >() ) { 1771 1770 if ( isCharType( pt->base ) ) { 1772 // strip cast if we're initializing a char[] with a char* 1771 // strip cast if we're initializing a char[] with a char* 1773 1772 // e.g. char x[] = "hello" 1774 1773 if ( auto ce = newExpr.as< ast::CastExpr >() ) { … … 1793 1792 assert( listInit->designations.size() == listInit->initializers.size() ); 1794 1793 for ( unsigned i = 0; i < listInit->designations.size(); ++i ) { 1795 // iterate designations and initializers in pairs, moving the cursor to the current 1794 // iterate designations and initializers in pairs, moving the cursor to the current 1796 1795 // designated object and resolving the initializer against that object 1797 1796 listInit = ast::mutate_field_index( 1798 listInit, &ast::ListInit::designations, i, 1797 listInit, &ast::ListInit::designations, i, 1799 1798 currentObject.findNext( listInit->designations[i] ) ); 1800 1799 listInit = ast::mutate_field_index( … … 1818 1817 ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::init, nullptr ); 1819 1818 1820 // intrinsic single-parameter constructors and destructors do nothing. Since this was 1821 // implicitly generated, there's no way for it to have side effects, so get rid of it to 1819 // intrinsic single-parameter constructors and destructors do nothing. Since this was 1820 // implicitly generated, there's no way for it to have side effects, so get rid of it to 1822 1821 // clean up generated code 1823 1822 if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->ctor ) ) {
Note: See TracChangeset
for help on using the changeset viewer.