Ignore:
Timestamp:
Jun 21, 2023, 2:38:55 AM (2 years ago)
Author:
JiadaL <j82liang@…>
Branches:
master
Children:
92355883
Parents:
0b0a285 (diff), 2de175ce (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.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/ResolvExpr/CandidateFinder.cpp

    r0b0a285 rc84dd61  
    3838#include "typeops.h"              // for combos
    3939#include "Unify.h"
     40#include "WidenMode.h"
    4041#include "AST/Expr.hpp"
    4142#include "AST/Node.hpp"
     
    749750                        // attempt to narrow based on expected target type
    750751                        const ast::Type * returnType = funcType->returns.front();
    751                         if ( ! unify(
    752                                 returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen )
    753                         ) {
    754                                 // unification failed, do not pursue this candidate
    755                                 return;
     752                        if ( selfFinder.strictMode ) {
     753                                if ( ! unifyExact(
     754                                        returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, noWiden() ) // xxx - is no widening correct?
     755                                ) {
     756                                        // unification failed, do not pursue this candidate
     757                                        return;
     758                                }
     759                        }
     760                        else {
     761                                if ( ! unify(
     762                                        returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen )
     763                                ) {
     764                                        // unification failed, do not pursue this candidate
     765                                        return;
     766                                }
    756767                        }
    757768                }
     
    771782                                for (size_t i=0; i<nParams; ++i) {
    772783                                        auto obj = funcDecl->params[i].strict_as<ast::ObjectDecl>();
    773                                         if (!instantiateArgument( location,
     784                                        if ( !instantiateArgument( location,
    774785                                                funcType->params[i], obj->init, args, results, genStart, symtab)) return;
    775786                                }
     
    781792                        // matches
    782793                        // no default args for indirect calls
    783                         if ( ! instantiateArgument( location,
     794                        if ( !instantiateArgument( location,
    784795                                param, nullptr, args, results, genStart, symtab ) ) return;
    785796                }
     
    874885
    875886                if ( auto structInst = aggrExpr->result.as< ast::StructInstType >() ) {
    876                         addAggMembers( structInst, aggrExpr, *cand, Cost::safe, "" );
     887                        addAggMembers( structInst, aggrExpr, *cand, Cost::unsafe, "" );
    877888                } else if ( auto unionInst = aggrExpr->result.as< ast::UnionInstType >() ) {
    878                         addAggMembers( unionInst, aggrExpr, *cand, Cost::safe, "" );
     889                        addAggMembers( unionInst, aggrExpr, *cand, Cost::unsafe, "" );
    879890                }
    880891        }
     
    10071018                                if ( auto pointer = dynamic_cast< const ast::PointerType * >( funcResult ) ) {
    10081019                                        if ( auto function = pointer->base.as< ast::FunctionType >() ) {
     1020                                                // if (!selfFinder.allowVoid && function->returns.empty()) continue;
    10091021                                                CandidateRef newFunc{ new Candidate{ *func } };
    10101022                                                newFunc->expr =
     
    10181030                                        if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) {
    10191031                                                if ( auto function = clz->bound.as< ast::FunctionType >() ) {
    1020                                                         CandidateRef newFunc{ new Candidate{ *func } };
     1032                                                        CandidateRef newFunc( new Candidate( *func ) );
    10211033                                                        newFunc->expr =
    10221034                                                                referenceToRvalueConversion( newFunc->expr, newFunc->cost );
     
    10601072                if ( found.empty() && ! errors.isEmpty() ) { throw errors; }
    10611073
     1074                // only keep the best matching intrinsic result to match C semantics (no unexpected narrowing/widening)
     1075                // TODO: keep one for each set of argument candidates?
     1076                Cost intrinsicCost = Cost::infinity;
     1077                CandidateList intrinsicResult;
     1078
    10621079                // Compute conversion costs
    10631080                for ( CandidateRef & withFunc : found ) {
     
    10821099                        if ( cvtCost != Cost::infinity ) {
    10831100                                withFunc->cvtCost = cvtCost;
    1084                                 candidates.emplace_back( std::move( withFunc ) );
    1085                         }
    1086                 }
     1101                                withFunc->cost += cvtCost;
     1102                                auto func = withFunc->expr.strict_as<ast::ApplicationExpr>()->func.as<ast::VariableExpr>();
     1103                                if (func && func->var->linkage == ast::Linkage::Intrinsic) {
     1104                                        if (withFunc->cost < intrinsicCost) {
     1105                                                intrinsicResult.clear();
     1106                                                intrinsicCost = withFunc->cost;
     1107                                        }
     1108                                        if (withFunc->cost == intrinsicCost) {
     1109                                                intrinsicResult.emplace_back(std::move(withFunc));
     1110                                        }
     1111                                }
     1112                                else {
     1113                                        candidates.emplace_back( std::move( withFunc ) );
     1114                                }
     1115                        }
     1116                }
     1117                spliceBegin( candidates, intrinsicResult );
    10871118                found = std::move( candidates );
    10881119
    10891120                // use a new list so that candidates are not examined by addAnonConversions twice
    1090                 CandidateList winners = findMinCost( found );
    1091                 promoteCvtCost( winners );
     1121                // CandidateList winners = findMinCost( found );
     1122                // promoteCvtCost( winners );
    10921123
    10931124                // function may return a struct/union value, in which case we need to add candidates
    10941125                // for implicit conversions to each of the anonymous members, which must happen after
    10951126                // `findMinCost`, since anon conversions are never the cheapest
    1096                 for ( const CandidateRef & c : winners ) {
     1127                for ( const CandidateRef & c : found ) {
    10971128                        addAnonConversions( c );
    10981129                }
    1099                 spliceBegin( candidates, winners );
    1100 
    1101                 if ( candidates.empty() && targetType && ! targetType->isVoid() ) {
     1130                // would this be too slow when we don't check cost anymore?
     1131                spliceBegin( candidates, found );
     1132
     1133                if ( candidates.empty() && targetType && ! targetType->isVoid() && !selfFinder.strictMode ) {
    11021134                        // If resolution is unsuccessful with a target type, try again without, since it
    11031135                        // will sometimes succeed when it wouldn't with a target type binding.
     
    11401172
    11411173                CandidateFinder finder( context, tenv, toType );
     1174                if (toType->isVoid()) {
     1175                        finder.allowVoid = true;
     1176                }
     1177                if ( castExpr->kind == ast::CastExpr::Return ) {
     1178                        finder.strictMode = true;
     1179                        finder.find( castExpr->arg, ResolvMode::withAdjustment() );
     1180
     1181                        // return casts are eliminated (merely selecting an overload, no actual operation)
     1182                        candidates = std::move(finder.candidates);
     1183                }
    11421184                finder.find( castExpr->arg, ResolvMode::withAdjustment() );
    11431185
     
    11451187
    11461188                CandidateList matches;
     1189                Cost minExprCost = Cost::infinity;
     1190                Cost minCastCost = Cost::infinity;
    11471191                for ( CandidateRef & cand : finder.candidates ) {
    11481192                        ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have;
     
    11761220                                // count one safe conversion for each value that is thrown away
    11771221                                thisCost.incSafe( discardedValues );
    1178                                 CandidateRef newCand = std::make_shared<Candidate>(
    1179                                         restructureCast( cand->expr, toType, castExpr->isGenerated ),
    1180                                         copy( cand->env ), std::move( open ), std::move( need ), cand->cost,
    1181                                         cand->cost + thisCost );
    1182                                 inferParameters( newCand, matches );
    1183                         }
    1184                 }
    1185 
    1186                 // select first on argument cost, then conversion cost
    1187                 CandidateList minArgCost = findMinCost( matches );
    1188                 promoteCvtCost( minArgCost );
    1189                 candidates = findMinCost( minArgCost );
     1222                                // select first on argument cost, then conversion cost
     1223                                if ( cand->cost < minExprCost || ( cand->cost == minExprCost && thisCost < minCastCost ) ) {
     1224                                        minExprCost = cand->cost;
     1225                                        minCastCost = thisCost;
     1226                                        matches.clear();
     1227
     1228
     1229                                }
     1230                                // ambiguous case, still output candidates to print in error message
     1231                                if ( cand->cost == minExprCost && thisCost == minCastCost ) {
     1232                                        CandidateRef newCand = std::make_shared<Candidate>(
     1233                                                restructureCast( cand->expr, toType, castExpr->isGenerated ),
     1234                                                copy( cand->env ), std::move( open ), std::move( need ), cand->cost + thisCost);
     1235                                        // currently assertions are always resolved immediately so this should have no effect.
     1236                                        // if this somehow changes in the future (e.g. delayed by indeterminate return type)
     1237                                        // we may need to revisit the logic.
     1238                                        inferParameters( newCand, matches );
     1239                                }
     1240                                // else skip, better alternatives found
     1241
     1242                        }
     1243                }
     1244                candidates = std::move(matches);
     1245
     1246                //CandidateList minArgCost = findMinCost( matches );
     1247                //promoteCvtCost( minArgCost );
     1248                //candidates = findMinCost( minArgCost );
    11901249        }
    11911250
     
    14531512                // candidates for true result
    14541513                CandidateFinder finder2( context, tenv );
     1514                finder2.allowVoid = true;
    14551515                finder2.find( conditionalExpr->arg2, ResolvMode::withAdjustment() );
    14561516                if ( finder2.candidates.empty() ) return;
     
    14581518                // candidates for false result
    14591519                CandidateFinder finder3( context, tenv );
     1520                finder3.allowVoid = true;
    14601521                finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() );
    14611522                if ( finder3.candidates.empty() ) return;
     
    15241585        void Finder::postvisit( const ast::ConstructorExpr * ctorExpr ) {
    15251586                CandidateFinder finder( context, tenv );
     1587                finder.allowVoid = true;
    15261588                finder.find( ctorExpr->callExpr, ResolvMode::withoutPrune() );
    15271589                for ( CandidateRef & r : finder.candidates ) {
     
    16401702                        CandidateFinder finder( context, tenv, toType );
    16411703                        finder.find( initExpr->expr, ResolvMode::withAdjustment() );
     1704
     1705                        Cost minExprCost = Cost::infinity;
     1706                        Cost minCastCost = Cost::infinity;
    16421707                        for ( CandidateRef & cand : finder.candidates ) {
    16431708                                if (reason.code == NotFound) reason.code = NoMatch;
     
    16771742                                        // count one safe conversion for each value that is thrown away
    16781743                                        thisCost.incSafe( discardedValues );
    1679                                         CandidateRef newCand = std::make_shared<Candidate>(
    1680                                                 new ast::InitExpr{
    1681                                                         initExpr->location, restructureCast( cand->expr, toType ),
    1682                                                         initAlt.designation },
    1683                                                 std::move(env), std::move( open ), std::move( need ), cand->cost, thisCost );
    1684                                         inferParameters( newCand, matches );
     1744                                        if ( cand->cost < minExprCost || ( cand->cost == minExprCost && thisCost < minCastCost ) ) {
     1745                                                minExprCost = cand->cost;
     1746                                                minCastCost = thisCost;
     1747                                                matches.clear();
     1748                                        }
     1749                                        // ambiguous case, still output candidates to print in error message
     1750                                        if ( cand->cost == minExprCost && thisCost == minCastCost ) {
     1751                                                CandidateRef newCand = std::make_shared<Candidate>(
     1752                                                        new ast::InitExpr{
     1753                                                                initExpr->location,
     1754                                                                restructureCast( cand->expr, toType ),
     1755                                                                initAlt.designation },
     1756                                                        std::move(env), std::move( open ), std::move( need ), cand->cost + thisCost );
     1757                                                // currently assertions are always resolved immediately so this should have no effect.
     1758                                                // if this somehow changes in the future (e.g. delayed by indeterminate return type)
     1759                                                // we may need to revisit the logic.
     1760                                                inferParameters( newCand, matches );
     1761                                        }
    16851762                                }
    16861763                        }
     
    16881765
    16891766                // select first on argument cost, then conversion cost
    1690                 CandidateList minArgCost = findMinCost( matches );
    1691                 promoteCvtCost( minArgCost );
    1692                 candidates = findMinCost( minArgCost );
     1767                // CandidateList minArgCost = findMinCost( matches );
     1768                // promoteCvtCost( minArgCost );
     1769                // candidates = findMinCost( minArgCost );
     1770                candidates = std::move(matches);
    16931771        }
    16941772
     
    17561834                        auto found = selected.find( mangleName );
    17571835                        if ( found != selected.end() ) {
    1758                                 if ( newCand->cost < found->second.candidate->cost ) {
     1836                                // tiebreaking by picking the lower cost on CURRENT expression
     1837                                // NOTE: this behavior is different from C semantics.
     1838                                // Specific remediations are performed for C operators at postvisit(UntypedExpr).
     1839                                // Further investigations may take place.
     1840                                if ( newCand->cost < found->second.candidate->cost
     1841                                        || (newCand->cost == found->second.candidate->cost && newCand->cvtCost < found->second.candidate->cvtCost) ) {
    17591842                                        PRINT(
    17601843                                                std::cerr << "cost " << newCand->cost << " beats "
     
    17631846
    17641847                                        found->second = PruneStruct{ newCand };
    1765                                 } else if ( newCand->cost == found->second.candidate->cost ) {
     1848                                } else if ( newCand->cost == found->second.candidate->cost && newCand->cvtCost == found->second.candidate->cvtCost ) {
    17661849                                        // if one of the candidates contains a deleted identifier, can pick the other,
    17671850                                        // since deleted expressions should not be ambiguous if there is another option
     
    18541937        */
    18551938
    1856         if ( mode.prune ) {
     1939        // optimization: don't prune for NameExpr since it never has cost
     1940        if ( mode.prune && !dynamic_cast<const ast::NameExpr *>(expr) ) {
    18571941                // trim candidates to single best one
    18581942                PRINT(
Note: See TracChangeset for help on using the changeset viewer.