Changeset 46da46b for src/ResolvExpr/CandidateFinder.cpp
- Timestamp:
- May 2, 2023, 3:44:31 AM (14 months ago)
- Branches:
- ast-experimental, master
- Children:
- 0c840fc
- Parents:
- 1ab773e0
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/ResolvExpr/CandidateFinder.cpp
r1ab773e0 r46da46b 30 30 #include "Resolver.h" 31 31 #include "ResolveTypeof.h" 32 #include "WidenMode.h" 32 33 #include "SatisfyAssertions.hpp" 33 34 #include "typeops.h" // for adjustExprType, conversionCost, polyCost, specCost … … 700 701 // attempt to narrow based on expected target type 701 702 const ast::Type * returnType = funcType->returns.front(); 702 if ( ! unify( 703 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab ) 704 ) { 705 // unification failed, do not pursue this candidate 706 return; 703 if ( selfFinder.strictMode ) { 704 if ( ! unifyExact( 705 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, noWiden(), symtab ) // xxx - is no widening correct? 706 ) { 707 // unification failed, do not pursue this candidate 708 return; 709 } 710 } 711 else { 712 if ( ! unify( 713 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, symtab ) 714 ) { 715 // unification failed, do not pursue this candidate 716 return; 717 } 707 718 } 708 719 } … … 825 836 826 837 if ( auto structInst = aggrExpr->result.as< ast::StructInstType >() ) { 827 addAggMembers( structInst, aggrExpr, *cand, Cost:: safe, "" );838 addAggMembers( structInst, aggrExpr, *cand, Cost::unsafe, "" ); 828 839 } else if ( auto unionInst = aggrExpr->result.as< ast::UnionInstType >() ) { 829 addAggMembers( unionInst, aggrExpr, *cand, Cost:: safe, "" );840 addAggMembers( unionInst, aggrExpr, *cand, Cost::unsafe, "" ); 830 841 } 831 842 } … … 958 969 if ( auto pointer = dynamic_cast< const ast::PointerType * >( funcResult ) ) { 959 970 if ( auto function = pointer->base.as< ast::FunctionType >() ) { 971 // if (!selfFinder.allowVoid && function->returns.empty()) continue; 960 972 CandidateRef newFunc{ new Candidate{ *func } }; 961 973 newFunc->expr = … … 1008 1020 if ( found.empty() && ! errors.isEmpty() ) { throw errors; } 1009 1021 1022 // only keep the best matching intrinsic result to match C semantics (no unexpected narrowing/widening) 1023 // TODO: keep one for each set of argument candidates? 1024 Cost intrinsicCost = Cost::infinity; 1025 CandidateList intrinsicResult; 1026 1010 1027 // Compute conversion costs 1011 1028 for ( CandidateRef & withFunc : found ) { … … 1030 1047 if ( cvtCost != Cost::infinity ) { 1031 1048 withFunc->cvtCost = cvtCost; 1032 candidates.emplace_back( std::move( withFunc ) ); 1033 } 1034 } 1049 withFunc->cost += cvtCost; 1050 auto func = withFunc->expr.strict_as<ast::ApplicationExpr>()->func.as<ast::VariableExpr>(); 1051 if (func && func->var->linkage == ast::Linkage::Intrinsic) { 1052 if (withFunc->cost < intrinsicCost) { 1053 intrinsicResult.clear(); 1054 intrinsicCost = withFunc->cost; 1055 } 1056 if (withFunc->cost == intrinsicCost) { 1057 intrinsicResult.emplace_back(std::move(withFunc)); 1058 } 1059 } 1060 else { 1061 candidates.emplace_back( std::move( withFunc ) ); 1062 } 1063 } 1064 } 1065 spliceBegin( candidates, intrinsicResult ); 1035 1066 found = std::move( candidates ); 1036 1067 1037 1068 // use a new list so that candidates are not examined by addAnonConversions twice 1038 CandidateList winners = findMinCost( found );1039 promoteCvtCost( winners );1069 // CandidateList winners = findMinCost( found ); 1070 // promoteCvtCost( winners ); 1040 1071 1041 1072 // function may return a struct/union value, in which case we need to add candidates 1042 1073 // for implicit conversions to each of the anonymous members, which must happen after 1043 1074 // `findMinCost`, since anon conversions are never the cheapest 1044 for ( const CandidateRef & c : winners) {1075 for ( const CandidateRef & c : found ) { 1045 1076 addAnonConversions( c ); 1046 1077 } 1047 spliceBegin( candidates, winners ); 1048 1049 if ( candidates.empty() && targetType && ! targetType->isVoid() ) { 1078 // would this be too slow when we don't check cost anymore? 1079 spliceBegin( candidates, found ); 1080 1081 if ( candidates.empty() && targetType && ! targetType->isVoid() && !selfFinder.strictMode ) { 1050 1082 // If resolution is unsuccessful with a target type, try again without, since it 1051 1083 // will sometimes succeed when it wouldn't with a target type binding. … … 1093 1125 1094 1126 CandidateFinder finder( context, tenv, toType ); 1127 if (toType->isVoid()) { 1128 finder.allowVoid = true; 1129 } 1130 if ( castExpr->kind == ast::CastExpr::Return ) { 1131 finder.strictMode = true; 1132 finder.find( castExpr->arg, ResolvMode::withAdjustment() ); 1133 1134 // return casts are eliminated (merely selecting an overload, no actual operation) 1135 candidates = std::move(finder.candidates); 1136 } 1095 1137 finder.find( castExpr->arg, ResolvMode::withAdjustment() ); 1096 1138 … … 1098 1140 1099 1141 CandidateList matches; 1142 Cost minExprCost = Cost::infinity; 1143 Cost minCastCost = Cost::infinity; 1100 1144 for ( CandidateRef & cand : finder.candidates ) { 1101 1145 ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have; … … 1129 1173 // count one safe conversion for each value that is thrown away 1130 1174 thisCost.incSafe( discardedValues ); 1131 CandidateRef newCand = std::make_shared<Candidate>( 1132 restructureCast( cand->expr, toType, castExpr->isGenerated ), 1133 copy( cand->env ), std::move( open ), std::move( need ), cand->cost, 1134 cand->cost + thisCost ); 1135 inferParameters( newCand, matches ); 1136 } 1137 } 1138 1139 // select first on argument cost, then conversion cost 1140 CandidateList minArgCost = findMinCost( matches ); 1141 promoteCvtCost( minArgCost ); 1142 candidates = findMinCost( minArgCost ); 1175 // select first on argument cost, then conversion cost 1176 if (cand->cost < minExprCost || cand->cost == minExprCost && thisCost < minCastCost) { 1177 minExprCost = cand->cost; 1178 minCastCost = thisCost; 1179 matches.clear(); 1180 1181 1182 } 1183 // ambiguous case, still output candidates to print in error message 1184 if (cand->cost == minExprCost && thisCost == minCastCost) { 1185 CandidateRef newCand = std::make_shared<Candidate>( 1186 restructureCast( cand->expr, toType, castExpr->isGenerated ), 1187 copy( cand->env ), std::move( open ), std::move( need ), cand->cost + thisCost); 1188 // currently assertions are always resolved immediately so this should have no effect. 1189 // if this somehow changes in the future (e.g. delayed by indeterminate return type) 1190 // we may need to revisit the logic. 1191 inferParameters( newCand, matches ); 1192 } 1193 // else skip, better alternatives found 1194 1195 } 1196 } 1197 candidates = std::move(matches); 1198 1199 //CandidateList minArgCost = findMinCost( matches ); 1200 //promoteCvtCost( minArgCost ); 1201 //candidates = findMinCost( minArgCost ); 1143 1202 } 1144 1203 … … 1261 1320 1262 1321 CandidateRef newCand = std::make_shared<Candidate>( 1263 newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero, 1264 cost ); 1322 newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, cost ); 1265 1323 1266 1324 if (newCand->expr->env) { … … 1407 1465 // candidates for true result 1408 1466 CandidateFinder finder2( context, tenv ); 1467 finder2.allowVoid = true; 1409 1468 finder2.find( conditionalExpr->arg2, ResolvMode::withAdjustment() ); 1410 1469 if ( finder2.candidates.empty() ) return; … … 1412 1471 // candidates for false result 1413 1472 CandidateFinder finder3( context, tenv ); 1473 finder3.allowVoid = true; 1414 1474 finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() ); 1415 1475 if ( finder3.candidates.empty() ) return; … … 1478 1538 void postvisit( const ast::ConstructorExpr * ctorExpr ) { 1479 1539 CandidateFinder finder( context, tenv ); 1540 finder.allowVoid = true; 1480 1541 finder.find( ctorExpr->callExpr, ResolvMode::withoutPrune() ); 1481 1542 for ( CandidateRef & r : finder.candidates ) { … … 1594 1655 CandidateFinder finder( context, tenv, toType ); 1595 1656 finder.find( initExpr->expr, ResolvMode::withAdjustment() ); 1657 1658 Cost minExprCost = Cost::infinity; 1659 Cost minCastCost = Cost::infinity; 1596 1660 for ( CandidateRef & cand : finder.candidates ) { 1597 1661 if(reason.code == NotFound) reason.code = NoMatch; … … 1631 1695 // count one safe conversion for each value that is thrown away 1632 1696 thisCost.incSafe( discardedValues ); 1633 CandidateRef newCand = std::make_shared<Candidate>( 1697 if (cand->cost < minExprCost || cand->cost == minExprCost && thisCost < minCastCost) { 1698 minExprCost = cand->cost; 1699 minCastCost = thisCost; 1700 matches.clear(); 1701 } 1702 // ambiguous case, still output candidates to print in error message 1703 if (cand->cost == minExprCost && thisCost == minCastCost) { 1704 CandidateRef newCand = std::make_shared<Candidate>( 1634 1705 new ast::InitExpr{ 1635 1706 initExpr->location, restructureCast( cand->expr, toType ), 1636 1707 initAlt.designation }, 1637 std::move(env), std::move( open ), std::move( need ), cand->cost, thisCost ); 1638 inferParameters( newCand, matches ); 1639 } 1708 std::move(env), std::move( open ), std::move( need ), cand->cost + thisCost ); 1709 // currently assertions are always resolved immediately so this should have no effect. 1710 // if this somehow changes in the future (e.g. delayed by indeterminate return type) 1711 // we may need to revisit the logic. 1712 inferParameters( newCand, matches ); 1713 } 1714 1715 } 1716 1640 1717 } 1641 1718 … … 1643 1720 1644 1721 // select first on argument cost, then conversion cost 1645 CandidateList minArgCost = findMinCost( matches ); 1646 promoteCvtCost( minArgCost ); 1647 candidates = findMinCost( minArgCost ); 1722 // CandidateList minArgCost = findMinCost( matches ); 1723 // promoteCvtCost( minArgCost ); 1724 // candidates = findMinCost( minArgCost ); 1725 candidates = std::move(matches); 1648 1726 } 1649 1727 … … 1724 1802 auto found = selected.find( mangleName ); 1725 1803 if ( found != selected.end() ) { 1726 if ( newCand->cost < found->second.candidate->cost ) { 1804 // tiebreaking by picking the lower cost on CURRENT expression 1805 // NOTE: this behavior is different from C semantics. 1806 // Specific remediations are performed for C operators at postvisit(UntypedExpr). 1807 // Further investigations may take place. 1808 if ( newCand->cost < found->second.candidate->cost 1809 || (newCand->cost == found->second.candidate->cost && newCand->cvtCost < found->second.candidate->cvtCost) ) { 1727 1810 PRINT( 1728 1811 std::cerr << "cost " << newCand->cost << " beats " … … 1731 1814 1732 1815 found->second = PruneStruct{ newCand }; 1733 } else if ( newCand->cost == found->second.candidate->cost ) {1816 } else if ( newCand->cost == found->second.candidate->cost && newCand->cvtCost == found->second.candidate->cvtCost) { 1734 1817 // if one of the candidates contains a deleted identifier, can pick the other, 1735 1818 // since deleted expressions should not be ambiguous if there is another option … … 1822 1905 */ 1823 1906 1824 if ( mode.prune ) { 1907 // if ( mode.prune ) { 1908 // optimization: don't prune for NameExpr since it never has cost 1909 if ( mode.prune && !dynamic_cast<const ast::NameExpr *>(expr)) { 1825 1910 // trim candidates to single best one 1826 1911 PRINT(
Note: See TracChangeset
for help on using the changeset viewer.