Ignore:
Timestamp:
May 2, 2023, 3:44:31 AM (14 months ago)
Author:
Fangren Yu <f37yu@…>
Branches:
ast-experimental, master
Children:
0c840fc
Parents:
1ab773e0
Message:

current progress

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/ResolvExpr/CandidateFinder.cpp

    r1ab773e0 r46da46b  
    3030#include "Resolver.h"
    3131#include "ResolveTypeof.h"
     32#include "WidenMode.h"
    3233#include "SatisfyAssertions.hpp"
    3334#include "typeops.h"              // for adjustExprType, conversionCost, polyCost, specCost
     
    700701                                // attempt to narrow based on expected target type
    701702                                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                                        }
    707718                                }
    708719                        }
     
    825836
    826837                        if ( auto structInst = aggrExpr->result.as< ast::StructInstType >() ) {
    827                                 addAggMembers( structInst, aggrExpr, *cand, Cost::safe, "" );
     838                                addAggMembers( structInst, aggrExpr, *cand, Cost::unsafe, "" );
    828839                        } else if ( auto unionInst = aggrExpr->result.as< ast::UnionInstType >() ) {
    829                                 addAggMembers( unionInst, aggrExpr, *cand, Cost::safe, "" );
     840                                addAggMembers( unionInst, aggrExpr, *cand, Cost::unsafe, "" );
    830841                        }
    831842                }
     
    958969                                        if ( auto pointer = dynamic_cast< const ast::PointerType * >( funcResult ) ) {
    959970                                                if ( auto function = pointer->base.as< ast::FunctionType >() ) {
     971                                                        // if (!selfFinder.allowVoid && function->returns.empty()) continue;
    960972                                                        CandidateRef newFunc{ new Candidate{ *func } };
    961973                                                        newFunc->expr =
     
    10081020                        if ( found.empty() && ! errors.isEmpty() ) { throw errors; }
    10091021
     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
    10101027                        // Compute conversion costs
    10111028                        for ( CandidateRef & withFunc : found ) {
     
    10301047                                if ( cvtCost != Cost::infinity ) {
    10311048                                        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 );
    10351066                        found = std::move( candidates );
    10361067
    10371068                        // 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 );
    10401071
    10411072                        // function may return a struct/union value, in which case we need to add candidates
    10421073                        // for implicit conversions to each of the anonymous members, which must happen after
    10431074                        // `findMinCost`, since anon conversions are never the cheapest
    1044                         for ( const CandidateRef & c : winners ) {
     1075                        for ( const CandidateRef & c : found ) {
    10451076                                addAnonConversions( c );
    10461077                        }
    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 ) {
    10501082                                // If resolution is unsuccessful with a target type, try again without, since it
    10511083                                // will sometimes succeed when it wouldn't with a target type binding.
     
    10931125
    10941126                        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                        }
    10951137                        finder.find( castExpr->arg, ResolvMode::withAdjustment() );
    10961138
     
    10981140
    10991141                        CandidateList matches;
     1142                        Cost minExprCost = Cost::infinity;
     1143                        Cost minCastCost = Cost::infinity;
    11001144                        for ( CandidateRef & cand : finder.candidates ) {
    11011145                                ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have;
     
    11291173                                        // count one safe conversion for each value that is thrown away
    11301174                                        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 );
    11431202                }
    11441203
     
    12611320
    12621321                                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 );
    12651323
    12661324                                if (newCand->expr->env) {
     
    14071465                        // candidates for true result
    14081466                        CandidateFinder finder2( context, tenv );
     1467                        finder2.allowVoid = true;
    14091468                        finder2.find( conditionalExpr->arg2, ResolvMode::withAdjustment() );
    14101469                        if ( finder2.candidates.empty() ) return;
     
    14121471                        // candidates for false result
    14131472                        CandidateFinder finder3( context, tenv );
     1473                        finder3.allowVoid = true;
    14141474                        finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() );
    14151475                        if ( finder3.candidates.empty() ) return;
     
    14781538                void postvisit( const ast::ConstructorExpr * ctorExpr ) {
    14791539                        CandidateFinder finder( context, tenv );
     1540                        finder.allowVoid = true;
    14801541                        finder.find( ctorExpr->callExpr, ResolvMode::withoutPrune() );
    14811542                        for ( CandidateRef & r : finder.candidates ) {
     
    15941655                                CandidateFinder finder( context, tenv, toType );
    15951656                                finder.find( initExpr->expr, ResolvMode::withAdjustment() );
     1657
     1658                                Cost minExprCost = Cost::infinity;
     1659                                Cost minCastCost = Cost::infinity;
    15961660                                for ( CandidateRef & cand : finder.candidates ) {
    15971661                                        if(reason.code == NotFound) reason.code = NoMatch;
     
    16311695                                                // count one safe conversion for each value that is thrown away
    16321696                                                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>(
    16341705                                                        new ast::InitExpr{
    16351706                                                                initExpr->location, restructureCast( cand->expr, toType ),
    16361707                                                                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                                       
    16401717                                }
    16411718
     
    16431720
    16441721                        // 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);
    16481726                }
    16491727
     
    17241802                        auto found = selected.find( mangleName );
    17251803                        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) ) {
    17271810                                        PRINT(
    17281811                                                std::cerr << "cost " << newCand->cost << " beats "
     
    17311814
    17321815                                        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) {
    17341817                                        // if one of the candidates contains a deleted identifier, can pick the other,
    17351818                                        // since deleted expressions should not be ambiguous if there is another option
     
    18221905        */
    18231906
    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)) {
    18251910                // trim candidates to single best one
    18261911                PRINT(
Note: See TracChangeset for help on using the changeset viewer.