- Timestamp:
- May 7, 2024, 7:04:17 PM (7 months ago)
- Branches:
- master
- Children:
- 0b6c1c9
- Parents:
- 164a6b6
- Location:
- src
- Files:
-
- 1 deleted
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
src/ResolvExpr/CandidateFinder.cpp
r164a6b6 rc333ed2 2138 2138 } 2139 2139 2140 // get the valueE(...) ApplicationExpr that returns the enum value2141 const ast::Expr * getValueEnumCall(2142 const ast::Expr * expr,2143 const ResolvExpr::ResolveContext & context, const ast::TypeEnvironment & env ) {2144 auto callExpr = new ast::UntypedExpr(2145 expr->location, new ast::NameExpr( expr->location, "valueE"), {expr} );2146 CandidateFinder finder( context, env );2147 finder.find( callExpr );2148 CandidateList winners = findMinCost( finder.candidates );2149 if (winners.size() != 1) {2150 SemanticError( callExpr, "Ambiguous expression in valueE..." );2151 }2152 CandidateRef & choice = winners.front();2153 return choice->expr;2154 }2155 2156 2140 const ast::Expr * createCondExpr( const ast::Expr * expr ) { 2157 2141 assert( expr ); -
src/ResolvExpr/CandidateFinder.hpp
r164a6b6 rc333ed2 70 70 const ast::Expr * expr, Cost & cost ); 71 71 72 /// Get the valueE application that returns the enum's value.73 const ast::Expr * getValueEnumCall( const ast::Expr * expr,74 const ResolveContext & context, const ast::TypeEnvironment & env );75 76 72 /// Wrap an expression to convert the result to a conditional result. 77 73 const ast::Expr * createCondExpr( const ast::Expr * expr ); -
src/ResolvExpr/CastCost.cc
r164a6b6 rc333ed2 54 54 cost = conversionCost( basicType, dst, srcIsLvalue, symtab, env ); 55 55 if ( Cost::unsafe < cost ) { 56 if (auto enumInst = 57 assert(enumInst->base->base);58 cost = Cost::unsafe;56 if (auto enumInst = dynamic_cast<const ast::EnumInstType *>(dst)) { 57 // Always explict cast only for typed enum 58 if (enumInst->base->isTyped) cost = Cost::unsafe; 59 59 } 60 60 } … … 63 63 64 64 void postvisit( const ast::ZeroType * zero ) { 65 // auto ptr = dynamic_cast< const ast::PointerType * >( dst );66 // if ( ptr && basicType->isInteger() ) {67 // // needed for, e.g. unsigned long => void *68 // cost = Cost::unsafe;69 // } else {70 65 cost = conversionCost( zero, dst, srcIsLvalue, symtab, env ); 71 66 if ( Cost::unsafe < cost ) { 72 67 if (auto enumInst = dynamic_cast<const ast::EnumInstType *>(dst)) { 73 assert(enumInst->base->base); 74 cost = Cost::unsafe; 68 if (enumInst->base->isTyped) cost = Cost::unsafe; 75 69 } 76 70 } 77 // }78 71 } 79 72 80 73 void postvisit( const ast::OneType * one ) { 81 // auto ptr = dynamic_cast< const ast::PointerType * >( dst );82 // if ( ptr && basicType->isInteger() ) {83 // // needed for, e.g. unsigned long => void *84 // cost = Cost::unsafe;85 // } else {86 74 cost = conversionCost( one, dst, srcIsLvalue, symtab, env ); 87 75 if ( Cost::unsafe < cost ) { 88 if (auto enumInst = dynamic_cast<const ast::EnumInstType *>(dst)) { 89 assert(enumInst->base->base); 90 cost = Cost::unsafe; 76 if (auto enumInst = dynamic_cast<const ast::EnumInstType *>(dst)) { 77 if (enumInst->base->isTyped) cost = Cost::unsafe; 91 78 } 92 79 } 93 // }94 80 } 95 81 -
src/ResolvExpr/CommonType.cc
r164a6b6 rc333ed2 650 650 651 651 void postvisit( const ast::EnumInstType * enumInst ) { 652 if ( enumInst->base && !enumInst->base-> base) {652 if ( enumInst->base && !enumInst->base->isTyped ) { 653 653 auto basicType = new ast::BasicType( ast::BasicKind::UnsignedInt ); 654 654 result = commonType( basicType, type2, tenv, need, have, open, widen); -
src/ResolvExpr/ConversionCost.cc
r164a6b6 rc333ed2 283 283 cost = costCalc( basicType, integer, srcIsLvalue, symtab, env ); 284 284 } else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) { 285 if ( dstAsEnumInst->base && !dstAsEnumInst->base-> base) {285 if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) { 286 286 cost = Cost::unsafe; 287 287 } … … 480 480 // assuming 0p is supposed to be used for pointers? 481 481 } else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) { 482 if ( dstAsEnumInst->base && !dstAsEnumInst->base-> base) {482 if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) { 483 483 cost = Cost::unsafe; 484 484 } … … 501 501 } 502 502 } else if ( auto dstAsEnumInst = dynamic_cast< const ast::EnumInstType * >( dst ) ) { 503 if ( dstAsEnumInst->base && !dstAsEnumInst->base-> base) {503 if ( dstAsEnumInst->base && !dstAsEnumInst->base->isTyped ) { 504 504 cost = Cost::unsafe; 505 505 } -
src/Validate/ImplementEnumFunc.cpp
r164a6b6 rc333ed2 28 28 proto_linkage{ast::Linkage::Cforall} {} 29 29 30 void genAttrFunctions();31 void genSuccPredPosn();32 // void genSuccPredDecl();33 34 void appendReturnThis(ast::FunctionDecl* decl) {35 assert(1 <= decl->params.size());36 assert(1 == decl->returns.size());37 assert(decl->stmts);38 39 const CodeLocation& location = (decl->stmts->kids.empty())40 ? decl->stmts->location41 : decl->stmts->kids.back()->location;42 const ast::DeclWithType* thisParam = decl->params.front();43 decl->stmts.get_and_mutate()->push_back(new ast::ReturnStmt(44 location, new ast::VariableExpr(location, thisParam)));45 }46 void genAttrStandardFuncs() {47 ast::FunctionDecl* (EnumAttrFuncGenerator::*standardProtos[4])()48 const = {&EnumAttrFuncGenerator::genCtorProto,49 &EnumAttrFuncGenerator::genCopyProto,50 &EnumAttrFuncGenerator::genDtorProto,51 &EnumAttrFuncGenerator::genAssignProto};52 for (auto& generator : standardProtos) {53 ast::FunctionDecl* decl = (this->*generator)();54 produceForwardDecl(decl);55 genFuncBody(decl);56 if (CodeGen::isAssignment(decl->name)) {57 appendReturnThis(decl);58 }59 produceDecl(decl);60 }61 }62 63 30 private: 64 31 const CodeLocation& getLocation() const { return decl->location; } … … 73 40 const ast::Decl* getDecl() const { return decl; } 74 41 75 // Implement Bounded trait for enum 76 void genBoundedFunctions(); 77 // Implement Serial trait for enum 42 // Implement Bounded trait 43 void genBoundedFunctions(); 44 ast::FunctionDecl* genBoundedProto(const char *) const; 45 void genBoundedBody(ast::FunctionDecl* func) const; 46 47 // Implement Serial trait 78 48 void genSerialTraitFuncs(); 79 80 // Bounded trait 81 ast::FunctionDecl* genLowerBoundProto() const; 82 ast::FunctionDecl* genUpperBoundProto() const; 83 void genLowerBoundBody(ast::FunctionDecl* func) const; 84 void genUpperBoundBody(ast::FunctionDecl* func) const; 85 49 ast::FunctionDecl* genFromIntProto() const; 50 ast::FunctionDecl* genFromInstanceProto() const; 51 ast::FunctionDecl* genInstToInstFuncProto(const char* func) const; 52 void genFromIntBody(ast::FunctionDecl *) const; 53 void genFromInstanceBody(ast::FunctionDecl *) const; 54 void genSuccPredBody(ast::FunctionDecl *, const char *) const; 55 56 // Implement TypedEnum trait 57 void genTypedEnumFuncs(); 58 void genTypedEnumFunction(const ast::EnumAttribute attr); 86 59 ast::FunctionDecl* genPosnProto() const; 87 60 ast::FunctionDecl* genLabelProto() const; 88 61 ast::FunctionDecl* genValueProto() const; 89 90 // Serial trait 91 ast::FunctionDecl* genFromIntProto() const; 92 ast::FunctionDecl* genFromInstanceProto() const; 93 ast::FunctionDecl* genSuccProto() const; 94 ast::FunctionDecl* genPredProto() const; 95 96 void genFromIntBody(ast::FunctionDecl *) const; 97 void genFromInstanceBody(ast::FunctionDecl *) const; 62 void genValueOrLabelBody( 63 ast::FunctionDecl* func, ast::ObjectDecl* arrDecl) const; 64 void genPosnBody(ast::FunctionDecl* func) const; 65 98 66 //////////////// 99 100 ast::FunctionDecl* genSuccPosProto() const;101 ast::FunctionDecl* genPredPosProto() const;102 67 103 68 // --------------------------------------------------- … … 121 86 } 122 87 123 /// E = EnumAttrType<T>`124 /// `void ?{}(E & _dst)`.125 ast::FunctionDecl* genCtorProto() const {126 return genProto("?{}", {dstParam()}, {});127 }128 129 /// void ?{}(E & _dst, E _src)`.130 ast::FunctionDecl* genCopyProto() const {131 return genProto("?{}", {dstParam(), srcParam()}, {});132 }133 134 ///`void ^?{}(E & _dst)`.135 ast::FunctionDecl* genDtorProto() const {136 // The destructor must be mutex on a concurrent type.137 return genProto("^?{}", {dstParam()}, {});138 }139 140 /// `E ?{}(E & _dst, E _src)`.141 ast::FunctionDecl* genAssignProto() const {142 // Only the name is different, so just reuse the generation function.143 auto retval = srcParam();144 retval->name = "_ret";145 return genProto("?=?", {dstParam(), srcParam()}, {retval});146 }147 148 void genFuncBody(ast::FunctionDecl* func) {149 const CodeLocation& location = func->location;150 auto& params = func->params;151 if (InitTweak::isCopyConstructor(func) ||152 InitTweak::isAssignment(func)) {153 assert(2 == params.size());154 auto dstParam = params.front().strict_as<ast::ObjectDecl>();155 auto srcParam = params.back().strict_as<ast::ObjectDecl>();156 func->stmts = genCopyBody(location, dstParam, srcParam);157 } else {158 assert(1 == params.size());159 // Default constructor and destructor is empty.160 func->stmts = new ast::CompoundStmt(location);161 // Add unused attribute to parameter to silence warnings.162 addUnusedAttribute(params.front());163 164 // Just an extra step to make the forward and declaration match.165 if (forwards.empty()) return;166 ast::FunctionDecl* fwd = strict_dynamic_cast<ast::FunctionDecl*>(167 forwards.back().get_and_mutate());168 addUnusedAttribute(fwd->params.front());169 }170 }171 172 const ast::CompoundStmt* genCopyBody( const CodeLocation& location,173 const ast::ObjectDecl* dstParam, const ast::ObjectDecl* srcParam) {174 return new ast::CompoundStmt(175 location,176 {new ast::ExprStmt(177 location,178 new ast::UntypedExpr(179 location, new ast::NameExpr(location, "__builtin_memcpy"),180 {181 new ast::AddressExpr( location,182 new ast::VariableExpr( location, dstParam ) ),183 new ast::AddressExpr( location,184 new ast::VariableExpr( location, srcParam ) ),185 new ast::SizeofExpr( location, srcParam->type ),186 }))});187 }188 189 void genDtorBody(ast::FunctionDecl* func) {190 const CodeLocation& location = func->location;191 auto& params = func->params;192 assert(1 == params.size());193 func->stmts = new ast::CompoundStmt(location);194 addUnusedAttribute(params.front());195 196 // Just an extra step to make the forward and declaration match.197 if (forwards.empty()) return;198 ast::FunctionDecl* fwd = strict_dynamic_cast<ast::FunctionDecl*>(199 forwards.back().get_and_mutate());200 addUnusedAttribute(fwd->params.front());201 }202 203 // ast::FunctionDecl*204 88 // ---------------------------------------------------- 205 206 ast::FunctionDecl* genSuccPredFunc(bool succ);207 89 208 90 const ast::Init* getAutoInit(const ast::Init* prev) const; … … 214 96 const ast::EnumAttribute attr, const CodeLocation& location, 215 97 std::vector<ast::ptr<ast::Init>>& inits) const; 216 void genValueOrLabelBody(217 ast::FunctionDecl* func, ast::ObjectDecl* arrDecl) const;218 void genPosnBody(ast::FunctionDecl* func) const;219 void genAttributesDecls(const ast::EnumAttribute attr);220 98 }; 221 99 … … 374 252 } 375 253 254 void EnumAttrFuncGenerator::genSuccPredBody(ast::FunctionDecl * func, const char* opt) const { 255 auto params = func->params; 256 assert( params.size() == 1 ); 257 auto param = params.front(); 258 auto enumToInt = new ast::CastExpr( 259 func->location, 260 new ast::VariableExpr(func->location, param), 261 new ast::BasicType(ast::BasicKind::UnsignedInt), 262 ast::GeneratedFlag::ExplicitCast 263 ); 264 ast::UntypedExpr* addOneExpr = ast::UntypedExpr::createCall( func->location, 265 opt, 266 {enumToInt, 267 ast::ConstantExpr::from_int(func->location, 1)} 268 ); 269 auto intToEnum = new ast::CastExpr( 270 func->location, 271 addOneExpr, 272 new ast::EnumInstType( decl ), 273 ast::GeneratedFlag::ExplicitCast 274 ); 275 func->stmts = new ast::CompoundStmt( 276 func->location, { 277 new ast::ReturnStmt( 278 func->location, 279 intToEnum 280 ) 281 } 282 ); 283 } 284 285 376 286 void EnumAttrFuncGenerator::genSerialTraitFuncs() { 377 auto fromIntProto = genFromIntProto(); 378 produceForwardDecl(fromIntProto); 379 genFromIntBody(fromIntProto); 380 produceDecl(fromIntProto); 381 382 auto fromInstanceProto = genFromInstanceProto(); 383 produceForwardDecl(fromInstanceProto); 384 genFromInstanceBody(fromInstanceProto); 385 produceDecl(fromInstanceProto); 386 387 auto succProto = genSuccProto(); 388 auto predProto = genPredProto(); 389 produceForwardDecl(succProto); 390 produceForwardDecl(predProto); 391 } 392 393 ast::FunctionDecl* EnumAttrFuncGenerator::genSuccProto() const { 287 ast::FunctionDecl * protos[4] = { 288 genFromIntProto(), 289 genFromInstanceProto(), 290 genInstToInstFuncProto("succ"), 291 genInstToInstFuncProto("pred") 292 }; 293 for (auto& proto: protos) produceForwardDecl(proto); 294 genFromIntBody(protos[0]); 295 genFromInstanceBody(protos[1]); 296 genSuccPredBody(protos[2], "?+?"); 297 genSuccPredBody(protos[3], "?-?"); 298 } 299 300 ast::FunctionDecl* EnumAttrFuncGenerator::genInstToInstFuncProto(const char * func) const { 394 301 return genProto( 395 "succ",302 func, 396 303 {new ast::ObjectDecl(getLocation(), "_i", new ast::EnumInstType(decl))}, 397 304 {new ast::ObjectDecl(getLocation(), "_ret", … … 399 306 } 400 307 401 ast::FunctionDecl* EnumAttrFuncGenerator::genPredProto() const { 402 return genProto( 403 "pred", 404 {new ast::ObjectDecl(getLocation(), "_i", new ast::EnumInstType(decl))}, 405 {new ast::ObjectDecl(getLocation(), "_ret", 406 new ast::EnumInstType(decl))}); 407 } 408 409 ast::FunctionDecl* EnumAttrFuncGenerator::genLowerBoundProto() const { 410 return genProto("lowerBound", {}, { 308 ast::FunctionDecl* EnumAttrFuncGenerator::genBoundedProto(const char * func) const { 309 return genProto(func, {}, { 411 310 new ast::ObjectDecl(getLocation(), "_i", new ast::EnumInstType(decl)) 412 311 }); 413 312 } 414 313 415 ast::FunctionDecl* EnumAttrFuncGenerator::genUpperBoundProto() const { 416 return genProto("upperBound", {}, { 417 new ast::ObjectDecl(getLocation(), "_i", new ast::EnumInstType(decl)) 418 }); 419 } 420 421 void EnumAttrFuncGenerator::genLowerBoundBody(ast::FunctionDecl* func) const { 314 void EnumAttrFuncGenerator::genBoundedBody(ast::FunctionDecl* func) const { 422 315 const CodeLocation & loc = func->location; 423 auto mem = decl->members.front(); 424 // auto expr = new ast::QualifiedNameExpr( loc, decl, mem->name ); 425 // expr->result = new ast::EnumInstType( decl ); 316 auto mem = func->name=="lowerBound"? decl->members.front() : decl->members.back(); 426 317 auto expr = new ast::NameExpr( loc, mem->name ); 427 318 func->stmts = new ast::CompoundStmt( loc, {new ast::ReturnStmt(loc, expr)}); 428 319 } 429 320 430 void EnumAttrFuncGenerator::genUpperBoundBody(ast::FunctionDecl* func) const {431 const CodeLocation & loc = func->location;432 auto mem = decl->members.back();433 auto expr = new ast::NameExpr( loc, mem->name );434 // expr->result = new ast::EnumInstType( decl );435 func->stmts = new ast::CompoundStmt( loc, {new ast::ReturnStmt(loc, expr)});436 }437 438 321 void EnumAttrFuncGenerator::genBoundedFunctions() { 439 ast::FunctionDecl * upperDecl = genUpperBoundProto(); 440 produceForwardDecl(upperDecl); 441 genUpperBoundBody(upperDecl); 442 produceDecl(upperDecl); 443 444 ast::FunctionDecl * lowerDecl = genLowerBoundProto(); 445 produceForwardDecl(lowerDecl); 446 genLowerBoundBody(lowerDecl); 447 produceDecl(lowerDecl); 322 ast::FunctionDecl * boundedProtos[2] = {genBoundedProto("upperBound"), genBoundedProto("lowerBound")}; 323 for (auto & protos: boundedProtos) { 324 produceForwardDecl(protos); 325 genBoundedBody(protos); 326 produceDecl(protos); 327 } 448 328 } 449 329 450 330 inline ast::EnumAttrType * getPosnType( const ast::EnumDecl * decl ) { 451 331 return new ast::EnumAttrType(new ast::EnumInstType(decl), ast::EnumAttribute::Posn); 452 }453 454 ast::FunctionDecl* EnumAttrFuncGenerator::genSuccPosProto() const {455 return genProto(456 "_successor_",457 {new ast::ObjectDecl(getLocation(), "_i", getPosnType(decl))},458 {new ast::ObjectDecl(getLocation(), "_ret", getPosnType(decl))}459 );460 }461 462 ast::FunctionDecl* EnumAttrFuncGenerator::genPredPosProto() const {463 return genProto(464 "_predessor_",465 {new ast::ObjectDecl(getLocation(), "_i", getPosnType(decl))},466 {new ast::ObjectDecl(getLocation(), "_ret", getPosnType(decl))}467 );468 332 } 469 333 … … 511 375 } 512 376 513 void EnumAttrFuncGenerator::gen AttributesDecls(const ast::EnumAttribute attr) {377 void EnumAttrFuncGenerator::genTypedEnumFunction(const ast::EnumAttribute attr) { 514 378 if (attr == ast::EnumAttribute::Value || 515 379 attr == ast::EnumAttribute::Label) { 380 // TypedEnum's backing arrays 516 381 std::vector<ast::ptr<ast::Init>> inits = 517 382 attr == ast::EnumAttribute::Value ? genValueInit() : genLabelInit(); … … 534 399 } 535 400 536 ast::FunctionDecl* EnumAttrFuncGenerator::genSuccPredFunc(bool succ) { 537 ast::FunctionDecl* funcDecl = succ ? genSuccPosProto() : genPredPosProto(); 538 produceForwardDecl(funcDecl); 539 540 const CodeLocation& location = getLocation(); 541 542 auto& params = funcDecl->params; 543 assert(params.size() == 1); 544 auto param = params.front().strict_as<ast::ObjectDecl>(); 545 546 547 auto rets = funcDecl->returns; 548 assert(params.size() == 1); 549 auto ret = rets.front().strict_as<ast::ObjectDecl>(); 550 auto retType = ret->type.strict_as<ast::EnumAttrType>(); 551 552 auto addOneExpr = ast::UntypedExpr::createCall( location, 553 succ? "?+?": "?-?", 554 {new ast::VariableExpr(location, param), 555 ast::ConstantExpr::from_int(location, 1)} 556 ); 557 558 funcDecl->stmts = new ast::CompoundStmt( 559 location, { 560 new ast::ReturnStmt( 561 location, 562 new ast::CastExpr(location, addOneExpr, retType) 563 ) 564 } 565 ); 566 567 return funcDecl; 568 } 569 570 void EnumAttrFuncGenerator::genAttrFunctions() { 571 genAttributesDecls(ast::EnumAttribute::Value); 572 genAttributesDecls(ast::EnumAttribute::Label); 573 genAttributesDecls(ast::EnumAttribute::Posn); 574 } 575 576 // void EnumAttrFuncGenerator::genSuccPredDecl() { 577 // auto succProto = genSuccProto(); 578 // auto predProto = genPredProto(); 579 580 // produceForwardDecl(succProto); 581 // produceForwardDecl(predProto); 582 // } 583 584 void EnumAttrFuncGenerator::genSuccPredPosn() { 585 ast::FunctionDecl* succ = genSuccPredFunc(true); 586 ast::FunctionDecl* pred = genSuccPredFunc(false); 587 588 produceDecl(succ); 589 produceDecl(pred); 401 void EnumAttrFuncGenerator::genTypedEnumFuncs() { 402 if (decl->base) genTypedEnumFunction(ast::EnumAttribute::Value); 403 genTypedEnumFunction(ast::EnumAttribute::Label); 404 genTypedEnumFunction(ast::EnumAttribute::Posn); 590 405 } 591 406 … … 593 408 std::list<ast::ptr<ast::Decl>>& decls) { 594 409 // Generate the functions (they go into forwards and definitions). 595 genAttrStandardFuncs(); 596 genAttrFunctions(); 410 genTypedEnumFuncs(); 597 411 genSerialTraitFuncs(); 598 genSuccPredPosn();599 // problematic600 412 genBoundedFunctions(); 601 413 // Now export the lists contents. … … 618 430 619 431 void ImplementEnumFunc::previsit(const ast::EnumDecl* enumDecl) { 620 if (!enumDecl->body) return; 621 if (!enumDecl->base) return; 622 432 if (!enumDecl->body || !enumDecl->isTyped) return; 623 433 ast::EnumInstType enumInst(enumDecl->name); 624 434 enumInst.base = enumDecl; 625 626 435 EnumAttrFuncGenerator gen(enumDecl, &enumInst, functionNesting); 627 436 gen.generateAndAppendFunctions(declsToAddAfter); -
src/Validate/module.mk
r164a6b6 rc333ed2 53 53 Validate/VerifyCtorDtorAssign.cpp \ 54 54 Validate/VerifyCtorDtorAssign.hpp \ 55 Validate/ReplacePseudoFunc.cpp \56 Validate/ReplacePseudoFunc.hpp \57 55 Validate/ImplementEnumFunc.cpp \ 58 56 Validate/ImplementEnumFunc.hpp -
src/main.cc
r164a6b6 rc333ed2 83 83 #include "Validate/ReturnCheck.hpp" // for checkReturnStatements 84 84 #include "Validate/VerifyCtorDtorAssign.hpp" // for verifyCtorDtorAssign 85 #include "Validate/ReplacePseudoFunc.hpp" // for replacePseudoFunc86 85 #include "Virtual/ExpandCasts.h" // for expandCasts 87 86 #include "Virtual/VirtualDtor.hpp" // for implementVirtDtors … … 383 382 PASS( "Resolve", ResolvExpr::resolve, transUnit ); 384 383 DUMP( exprp, std::move( transUnit ) ); 385 PASS( "Replace Pseudo Func", Validate::replacePseudoFunc, transUnit );386 384 PASS( "Fix Init", InitTweak::fix, transUnit, buildingLibrary() ); // Here 387 385 PASS( "Erase With", ResolvExpr::eraseWith, transUnit );
Note: See TracChangeset
for help on using the changeset viewer.