Changeset 8d182b1 for src/ResolvExpr/ConversionCost.cc
- Timestamp:
- Nov 14, 2023, 12:19:09 PM (23 months ago)
- Branches:
- master
- Children:
- 1ccae59, 89a8bab
- Parents:
- df8ba61a (diff), 5625427 (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/ConversionCost.cc
rdf8ba61a r8d182b1 21 21 22 22 #include "ResolvExpr/Cost.h" // for Cost 23 #include "ResolvExpr/TypeEnvironment.h" // for EqvClass, TypeEnvironment24 23 #include "ResolvExpr/Unify.h" // for typesCompatibleIgnoreQualifiers 25 24 #include "ResolvExpr/PtrsAssignable.hpp" // for ptrsAssignable 26 #include "SymTab/Indexer.h" // for Indexer27 #include "SynTree/Declaration.h" // for TypeDecl, NamedTypeDecl28 #include "SynTree/Type.h" // for Type, BasicType, TypeInstType29 30 25 31 26 namespace ResolvExpr { 32 #if 033 const Cost Cost::zero = Cost{ 0, 0, 0, 0, 0, 0, 0 };34 const Cost Cost::infinity = Cost{ -1, -1, -1, -1, -1, 1, -1 };35 const Cost Cost::unsafe = Cost{ 1, 0, 0, 0, 0, 0, 0 };36 const Cost Cost::poly = Cost{ 0, 1, 0, 0, 0, 0, 0 };37 const Cost Cost::safe = Cost{ 0, 0, 1, 0, 0, 0, 0 };38 const Cost Cost::sign = Cost{ 0, 0, 0, 1, 0, 0, 0 };39 const Cost Cost::var = Cost{ 0, 0, 0, 0, 1, 0, 0 };40 const Cost Cost::spec = Cost{ 0, 0, 0, 0, 0, -1, 0 };41 const Cost Cost::reference = Cost{ 0, 0, 0, 0, 0, 0, 1 };42 #endif43 27 44 28 #if 0 … … 47 31 #define PRINT(x) 48 32 #endif 49 50 Cost conversionCost( const Type * src, const Type * dest, bool srcIsLvalue,51 const SymTab::Indexer &indexer, const TypeEnvironment &env ) {52 if ( const TypeInstType * destAsTypeInst = dynamic_cast< const TypeInstType * >( dest ) ) {53 PRINT( std::cerr << "type inst " << destAsTypeInst->name; )54 if ( const EqvClass * eqvClass = env.lookup( destAsTypeInst->name ) ) {55 if ( eqvClass->type ) {56 return conversionCost( src, eqvClass->type, srcIsLvalue, indexer, env );57 } else {58 return Cost::infinity;59 }60 } else if ( const NamedTypeDecl * namedType = indexer.lookupType( destAsTypeInst->name ) ) {61 PRINT( std::cerr << " found" << std::endl; )62 const TypeDecl * type = dynamic_cast< const TypeDecl * >( namedType );63 // all typedefs should be gone by this point64 assert( type );65 if ( type->base ) {66 return conversionCost( src, type->base, srcIsLvalue, indexer, env )67 + Cost::safe;68 } // if69 } // if70 PRINT( std::cerr << " not found" << std::endl; )71 } // if72 PRINT(73 std::cerr << "src is ";74 src->print( std::cerr );75 std::cerr << std::endl << "dest is ";76 dest->print( std::cerr );77 std::cerr << std::endl << "env is" << std::endl;78 env.print( std::cerr, 8 );79 )80 if ( typesCompatibleIgnoreQualifiers( src, dest, indexer, env ) ) {81 PRINT( std::cerr << "compatible!" << std::endl; )82 return Cost::zero;83 } else if ( dynamic_cast< const VoidType * >( dest ) ) {84 return Cost::safe;85 } else if ( const ReferenceType * refType = dynamic_cast< const ReferenceType * > ( dest ) ) {86 PRINT( std::cerr << "conversionCost: dest is reference" << std::endl; )87 return convertToReferenceCost( src, refType, srcIsLvalue, indexer, env, [](const Type * const t1, const Type * t2, const SymTab::Indexer &, const TypeEnvironment & env ){88 return ptrsAssignable( t1, t2, env );89 });90 } else {91 PassVisitor<ConversionCost> converter(92 dest, srcIsLvalue, indexer, env,93 (Cost (*)(const Type *, const Type *, bool, const SymTab::Indexer&, const TypeEnvironment&))94 conversionCost );95 src->accept( converter );96 if ( converter.pass.get_cost() == Cost::infinity ) {97 return Cost::infinity;98 } else {99 return converter.pass.get_cost() + Cost::zero;100 } // if101 } // if102 }103 104 static Cost convertToReferenceCost( const Type * src, const Type * dest, bool srcIsLvalue,105 int diff, const SymTab::Indexer & indexer, const TypeEnvironment & env, PtrsFunction func ) {106 PRINT( std::cerr << "convert to reference cost... diff " << diff << " " << src << " / " << dest << std::endl; )107 if ( diff > 0 ) {108 // TODO: document this109 Cost cost = convertToReferenceCost(110 strict_dynamic_cast< const ReferenceType * >( src )->base, dest, srcIsLvalue,111 diff-1, indexer, env, func );112 cost.incReference();113 return cost;114 } else if ( diff < -1 ) {115 // TODO: document this116 Cost cost = convertToReferenceCost(117 src, strict_dynamic_cast< const ReferenceType * >( dest )->base, srcIsLvalue,118 diff+1, indexer, env, func );119 cost.incReference();120 return cost;121 } else if ( diff == 0 ) {122 const ReferenceType * srcAsRef = dynamic_cast< const ReferenceType * >( src );123 const ReferenceType * destAsRef = dynamic_cast< const ReferenceType * >( dest );124 if ( srcAsRef && destAsRef ) { // pointer-like conversions between references125 PRINT( std::cerr << "converting between references" << std::endl; )126 Type::Qualifiers tq1 = srcAsRef->base->tq;127 Type::Qualifiers tq2 = destAsRef->base->tq;128 if ( tq1 <= tq2 && typesCompatibleIgnoreQualifiers( srcAsRef->base, destAsRef->base, indexer, env ) ) {129 PRINT( std::cerr << " :: compatible and good qualifiers" << std::endl; )130 if ( tq1 == tq2 ) {131 // types are the same132 return Cost::zero;133 } else {134 // types are the same, except otherPointer has more qualifiers135 return Cost::safe;136 }137 } else { // xxx - this discards reference qualifiers from consideration -- reducing qualifiers is a safe conversion; is this right?138 int assignResult = func( srcAsRef->base, destAsRef->base, indexer, env );139 PRINT( std::cerr << "comparing references: " << assignResult << " " << srcAsRef << " " << destAsRef << std::endl; )140 if ( assignResult > 0 ) {141 return Cost::safe;142 } else if ( assignResult < 0 ) {143 return Cost::unsafe;144 } // if145 } // if146 } else {147 PRINT( std::cerr << "reference to rvalue conversion" << std::endl; )148 PassVisitor<ConversionCost> converter(149 dest, srcIsLvalue, indexer, env,150 (Cost (*)(const Type *, const Type *, bool, const SymTab::Indexer&, const TypeEnvironment&))151 conversionCost );152 src->accept( converter );153 return converter.pass.get_cost();154 } // if155 } else {156 const ReferenceType * destAsRef = dynamic_cast< const ReferenceType * >( dest );157 assert( diff == -1 && destAsRef );158 PRINT( std::cerr << "dest is: " << dest << " / src is: " << src << std::endl; )159 if ( typesCompatibleIgnoreQualifiers( src, destAsRef->base, indexer, env ) ) {160 PRINT( std::cerr << "converting compatible base type" << std::endl; )161 if ( srcIsLvalue ) {162 PRINT(163 std::cerr << "lvalue to reference conversion" << std::endl;164 std::cerr << src << " => " << destAsRef << std::endl;165 )166 // lvalue-to-reference conversion: cv lvalue T => cv T &167 if ( src->tq == destAsRef->base->tq ) {168 return Cost::reference; // cost needs to be non-zero to add cast169 } if ( src->tq < destAsRef->base->tq ) {170 return Cost::safe; // cost needs to be higher than previous cast to differentiate adding qualifiers vs. keeping same171 } else {172 return Cost::unsafe;173 } // if174 } else if ( destAsRef->base->get_const() ) {175 PRINT( std::cerr << "rvalue to const ref conversion" << std::endl; )176 // rvalue-to-const-reference conversion: T => const T &177 return Cost::safe;178 } else {179 PRINT( std::cerr << "rvalue to non-const reference conversion" << std::endl; )180 // rvalue-to-reference conversion: T => T &181 return Cost::unsafe;182 } // if183 } // if184 PRINT( std::cerr << "attempting to convert from incompatible base type -- fail" << std::endl; )185 }186 return Cost::infinity;187 }188 189 Cost convertToReferenceCost( const Type * src, const ReferenceType * dest, bool srcIsLvalue,190 const SymTab::Indexer & indexer, const TypeEnvironment & env, PtrsFunction func ) {191 int sdepth = src->referenceDepth(), ddepth = dest->referenceDepth();192 Cost cost = convertToReferenceCost( src, dest, srcIsLvalue, sdepth-ddepth, indexer, env, func );193 PRINT( std::cerr << "convertToReferenceCost result: " << cost << std::endl; )194 return cost;195 }196 197 ConversionCost::ConversionCost( const Type * dest, bool srcIsLvalue, const SymTab::Indexer &indexer, const TypeEnvironment &env, CostFunction costFunc )198 : dest( dest ), srcIsLvalue( srcIsLvalue ), indexer( indexer ), cost( Cost::infinity ), env( env ), costFunc( costFunc ) {199 }200 33 201 34 // GENERATED START, DO NOT EDIT … … 319 152 ); 320 153 321 void ConversionCost::postvisit( const VoidType * ) {322 cost = Cost::infinity;323 }324 325 // refactor for code resue326 void ConversionCost::conversionCostFromBasicToBasic(const BasicType * src, const BasicType * dest) {327 int tableResult = costMatrix[ src->kind ][ dest->kind ];328 if ( tableResult == -1 ) {329 cost = Cost::unsafe;330 } else {331 cost = Cost::zero;332 cost.incSafe( tableResult );333 cost.incSign( signMatrix[ src->kind ][ dest->kind ] );334 } // if335 } // ConversionCost::conversionCostFromBasicToBasic336 337 void ConversionCost::postvisit(const BasicType * basicType) {338 if ( const BasicType * destAsBasic = dynamic_cast< const BasicType * >( dest ) ) {339 conversionCostFromBasicToBasic(basicType, destAsBasic);340 } else if ( const EnumInstType * enumInst = dynamic_cast< const EnumInstType * >( dest ) ) {341 const EnumDecl * base_enum = enumInst->baseEnum;342 if ( const Type * base = base_enum->base ) {343 if ( const BasicType * enumBaseAstBasic = dynamic_cast< const BasicType *> (base) ) {344 conversionCostFromBasicToBasic(basicType, enumBaseAstBasic);345 } else {346 cost = Cost::infinity;347 } // if348 } else {349 cost = Cost::unsafe;350 } // if351 } // if352 // no cases for zero_t/one_t because it should not be possible to convert int, etc. to zero_t/one_t.353 }354 355 void ConversionCost::postvisit( const PointerType * pointerType ) {356 if ( const PointerType * destAsPtr = dynamic_cast< const PointerType * >( dest ) ) {357 PRINT( std::cerr << pointerType << " ===> " << destAsPtr << std::endl; )358 Type::Qualifiers tq1 = pointerType->base->tq;359 Type::Qualifiers tq2 = destAsPtr->base->tq;360 if ( tq1 <= tq2 && typesCompatibleIgnoreQualifiers( pointerType->base, destAsPtr->base, indexer, env ) ) {361 PRINT( std::cerr << " :: compatible and good qualifiers" << std::endl; )362 if ( tq1 == tq2 ) {363 // types are the same364 cost = Cost::zero;365 } else {366 // types are the same, except otherPointer has more qualifiers367 cost = Cost::safe;368 } // if369 } else {370 int assignResult = ptrsAssignable( pointerType->base, destAsPtr->base, env );371 PRINT( std::cerr << " :: " << assignResult << std::endl; )372 if ( assignResult > 0 && tq1 <= tq2 ) {373 // xxx - want the case where qualifiers are added to be more expensive than the case where qualifiers are the same. Is 1 safe vs. 2 safe correct?374 if ( tq1 == tq2 ) {375 cost = Cost::safe;376 } else if ( tq1 < tq2 ) {377 cost = Cost::safe+Cost::safe;378 }379 } else if ( assignResult < 0 ) {380 cost = Cost::unsafe;381 } // if382 // assignResult == 0 means Cost::Infinity383 } // if384 // case case for zero_t because it should not be possible to convert pointers to zero_t.385 } // if386 }387 388 void ConversionCost::postvisit( const ArrayType * ) {}389 390 void ConversionCost::postvisit( const ReferenceType * refType ) {391 // Note: dest can never be a reference, since it would have been caught in an earlier check392 assert( ! dynamic_cast< const ReferenceType * >( dest ) );393 // convert reference to rvalue: cv T1 & => T2394 // recursively compute conversion cost from T1 to T2.395 // cv can be safely dropped because of 'implicit dereference' behavior.396 cost = costFunc( refType->base, dest, srcIsLvalue, indexer, env );397 if ( refType->base->tq == dest->tq ) {398 cost.incReference(); // prefer exact qualifiers399 } else if ( refType->base->tq < dest->tq ) {400 cost.incSafe(); // then gaining qualifiers401 } else {402 cost.incUnsafe(); // lose qualifiers as last resort403 }404 PRINT( std::cerr << refType << " ==> " << dest << " " << cost << std::endl; )405 }406 407 void ConversionCost::postvisit( const FunctionType * ) {}408 409 void ConversionCost::postvisit( const EnumInstType * enumInst) {410 const EnumDecl * enumDecl = enumInst -> baseEnum;411 if ( const Type * enumType = enumDecl -> base ) { // if it is a typed enum412 cost = costFunc( enumType, dest, srcIsLvalue, indexer, env );413 } else {414 static Type::Qualifiers q;415 static BasicType integer( q, BasicType::SignedInt );416 cost = costFunc( &integer, dest, srcIsLvalue, indexer, env ); // safe if dest >= int417 } // if418 if ( cost < Cost::unsafe ) {419 cost.incSafe();420 } // if421 }422 423 void ConversionCost::postvisit( const TraitInstType * ) {}424 425 void ConversionCost::postvisit( const TypeInstType * inst ) {426 if ( const EqvClass * eqvClass = env.lookup( inst->name ) ) {427 cost = costFunc( eqvClass->type, dest, srcIsLvalue, indexer, env );428 } else if ( const TypeInstType * destAsInst = dynamic_cast< const TypeInstType * >( dest ) ) {429 if ( inst->name == destAsInst->name ) {430 cost = Cost::zero;431 }432 } else if ( const NamedTypeDecl * namedType = indexer.lookupType( inst->name ) ) {433 const TypeDecl * type = dynamic_cast< const TypeDecl * >( namedType );434 // all typedefs should be gone by this point435 assert( type );436 if ( type->base ) {437 cost = costFunc( type->base, dest, srcIsLvalue, indexer, env ) + Cost::safe;438 } // if439 } // if440 }441 442 void ConversionCost::postvisit( const TupleType * tupleType ) {443 Cost c = Cost::zero;444 if ( const TupleType * destAsTuple = dynamic_cast< const TupleType * >( dest ) ) {445 std::list< Type * >::const_iterator srcIt = tupleType->types.begin();446 std::list< Type * >::const_iterator destIt = destAsTuple->types.begin();447 while ( srcIt != tupleType->types.end() && destIt != destAsTuple->types.end() ) {448 Cost newCost = costFunc( * srcIt++, * destIt++, srcIsLvalue, indexer, env );449 if ( newCost == Cost::infinity ) {450 return;451 } // if452 c += newCost;453 } // while454 if ( destIt != destAsTuple->types.end() ) {455 cost = Cost::infinity;456 } else {457 cost = c;458 } // if459 } // if460 }461 462 void ConversionCost::postvisit( const VarArgsType * ) {463 if ( dynamic_cast< const VarArgsType * >( dest ) ) {464 cost = Cost::zero;465 }466 }467 468 void ConversionCost::postvisit( const ZeroType * ) {469 if ( dynamic_cast< const ZeroType * >( dest ) ) {470 cost = Cost::zero;471 } else if ( const BasicType * destAsBasic = dynamic_cast< const BasicType * >( dest ) ) {472 // copied from visit(BasicType *) for signed int, but +1 for safe conversions473 int tableResult = costMatrix[ BasicType::SignedInt ][ destAsBasic->kind ];474 if ( tableResult == -1 ) {475 cost = Cost::unsafe;476 } else {477 cost = Cost::zero;478 cost.incSafe( tableResult + 1 );479 cost.incSign( signMatrix[ BasicType::SignedInt ][ destAsBasic->kind ] );480 } // if481 } else if ( dynamic_cast< const PointerType * >( dest ) ) {482 cost = Cost::zero;483 cost.incSafe( maxIntCost + 2 ); // +1 for zero_t -> int, +1 for disambiguation484 } // if485 }486 487 void ConversionCost::postvisit( const OneType * ) {488 if ( dynamic_cast< const OneType * >( dest ) ) {489 cost = Cost::zero;490 } else if ( const BasicType * destAsBasic = dynamic_cast< const BasicType * >( dest ) ) {491 // copied from visit(BasicType *) for signed int, but +1 for safe conversions492 int tableResult = costMatrix[ BasicType::SignedInt ][ destAsBasic->kind ];493 if ( tableResult == -1 ) {494 cost = Cost::unsafe;495 } else {496 cost = Cost::zero;497 cost.incSafe( tableResult + 1 );498 cost.incSign( signMatrix[ BasicType::SignedInt ][ destAsBasic->kind ] );499 } // if500 } // if501 }502 503 154 namespace { 504 # warning For overload resolution between the two versions.505 155 int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2, 506 156 const ast::SymbolTable &, const ast::TypeEnvironment & env ) { 507 157 return ptrsAssignable( t1, t2, env ); 508 158 } 509 Cost localConversionCost(510 const ast::Type * src, const ast::Type * dst, bool srcIsLvalue,511 const ast::SymbolTable & symtab, const ast::TypeEnvironment & env512 ) { return conversionCost( src, dst, srcIsLvalue, symtab, env ); }513 159 } 514 160 … … 540 186 return convertToReferenceCost( src, refType, srcIsLvalue, symtab, env, localPtrsAssignable ); 541 187 } else { 542 return ast::Pass<ConversionCost _new>::read( src, dst, srcIsLvalue, symtab, env, localConversionCost );188 return ast::Pass<ConversionCost>::read( src, dst, srcIsLvalue, symtab, env, conversionCost ); 543 189 } 544 190 } … … 581 227 } 582 228 } else { 583 return ast::Pass<ConversionCost _new>::read( src, dst, srcIsLvalue, symtab, env, localConversionCost );229 return ast::Pass<ConversionCost>::read( src, dst, srcIsLvalue, symtab, env, conversionCost ); 584 230 } 585 231 } else { … … 613 259 } 614 260 615 void ConversionCost _new::postvisit( const ast::VoidType * voidType ) {261 void ConversionCost::postvisit( const ast::VoidType * voidType ) { 616 262 (void)voidType; 617 263 cost = Cost::infinity; 618 264 } 619 265 620 void ConversionCost _new::conversionCostFromBasicToBasic( const ast::BasicType * src, const ast::BasicType* dest ) {266 void ConversionCost::conversionCostFromBasicToBasic( const ast::BasicType * src, const ast::BasicType* dest ) { 621 267 int tableResult = costMatrix[ src->kind ][ dest->kind ]; 622 268 if ( tableResult == -1 ) { … … 629 275 } 630 276 631 void ConversionCost _new::postvisit( const ast::BasicType * basicType ) {277 void ConversionCost::postvisit( const ast::BasicType * basicType ) { 632 278 if ( const ast::BasicType * dstAsBasic = dynamic_cast< const ast::BasicType * >( dst ) ) { 633 279 conversionCostFromBasicToBasic( basicType, dstAsBasic ); … … 635 281 const ast::EnumDecl * enumDecl = enumInst->base.get(); 636 282 if ( enumDecl->isTyped && !enumDecl->base.get() ) { 637 cost = Cost::infinity; 283 cost = Cost::infinity; 638 284 } else if ( const ast::Type * enumType = enumDecl->base.get() ) { 639 285 if ( const ast::BasicType * enumTypeAsBasic = dynamic_cast<const ast::BasicType *>(enumType) ) { … … 648 294 } 649 295 650 void ConversionCost _new::postvisit( const ast::PointerType * pointerType ) {296 void ConversionCost::postvisit( const ast::PointerType * pointerType ) { 651 297 if ( const ast::PointerType * dstAsPtr = dynamic_cast< const ast::PointerType * >( dst ) ) { 652 298 ast::CV::Qualifiers tq1 = pointerType->base->qualifiers; … … 694 340 } 695 341 696 void ConversionCost _new::postvisit( const ast::ArrayType * arrayType ) {342 void ConversionCost::postvisit( const ast::ArrayType * arrayType ) { 697 343 (void)arrayType; 698 344 } 699 345 700 void ConversionCost _new::postvisit( const ast::ReferenceType * refType ) {346 void ConversionCost::postvisit( const ast::ReferenceType * refType ) { 701 347 assert( nullptr == dynamic_cast< const ast::ReferenceType * >( dst ) ); 702 348 … … 716 362 } 717 363 718 void ConversionCost _new::postvisit( const ast::FunctionType * functionType ) {364 void ConversionCost::postvisit( const ast::FunctionType * functionType ) { 719 365 (void)functionType; 720 366 } 721 367 722 void ConversionCost _new::postvisit( const ast::EnumInstType * enumInstType ) {368 void ConversionCost::postvisit( const ast::EnumInstType * enumInstType ) { 723 369 const ast::EnumDecl * baseEnum = enumInstType->base; 724 370 if ( const ast::Type * baseType = baseEnum->base ) { … … 733 379 } 734 380 735 void ConversionCost _new::postvisit( const ast::TraitInstType * traitInstType ) {381 void ConversionCost::postvisit( const ast::TraitInstType * traitInstType ) { 736 382 (void)traitInstType; 737 383 } 738 384 739 void ConversionCost _new::postvisit( const ast::TypeInstType * typeInstType ) {385 void ConversionCost::postvisit( const ast::TypeInstType * typeInstType ) { 740 386 if ( const ast::EqvClass * eqv = env.lookup( *typeInstType ) ) { 741 387 cost = costCalc( eqv->bound, dst, srcIsLvalue, symtab, env ); … … 754 400 } 755 401 756 void ConversionCost _new::postvisit( const ast::TupleType * tupleType ) {402 void ConversionCost::postvisit( const ast::TupleType * tupleType ) { 757 403 Cost c = Cost::zero; 758 404 if ( const ast::TupleType * dstAsTuple = dynamic_cast< const ast::TupleType * >( dst ) ) { … … 776 422 } 777 423 778 void ConversionCost _new::postvisit( const ast::VarArgsType * varArgsType ) {424 void ConversionCost::postvisit( const ast::VarArgsType * varArgsType ) { 779 425 (void)varArgsType; 780 426 if ( dynamic_cast< const ast::VarArgsType * >( dst ) ) { … … 783 429 } 784 430 785 void ConversionCost _new::postvisit( const ast::ZeroType * zeroType ) {431 void ConversionCost::postvisit( const ast::ZeroType * zeroType ) { 786 432 (void)zeroType; 787 433 if ( dynamic_cast< const ast::ZeroType * >( dst ) ) { … … 810 456 } 811 457 812 void ConversionCost _new::postvisit( const ast::OneType * oneType ) {458 void ConversionCost::postvisit( const ast::OneType * oneType ) { 813 459 (void)oneType; 814 460 if ( dynamic_cast< const ast::OneType * >( dst ) ) { 815 461 cost = Cost::zero; 816 462 } else if ( const ast::BasicType * dstAsBasic = 817 dynamic_cast< const ast::BasicType * >( dst ) ) { 463 dynamic_cast< const ast::BasicType * >( dst ) ) { 818 464 int tableResult = costMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ]; 819 465 if ( -1 == tableResult ) { … … 824 470 cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] ); 825 471 } 826 827 // cost = Cost::zero; 828 } 829 } 830 // size_t ConversionCost_new::traceId = Stats::Heap::new_stacktrace_id("ConversionCost"); 472 } 473 } 474 475 // size_t ConversionCost::traceId = Stats::Heap::new_stacktrace_id("ConversionCost"); 831 476 832 477 } // namespace ResolvExpr
Note:
See TracChangeset
for help on using the changeset viewer.