Goose  Artifact [3f47f2d5c9]

Artifact 3f47f2d5c9f32f56fd58ad7fab7ee4d1b001256c03d0e91a778e6a4e3e6cb8d8:

  • File bs/builtins/types/overloadset/invoke.cpp — part of check-in [4ece399a6f] at 2021-04-11 13:10:56 on branch trunk — Implemented a way to track down (in debug builds) which typechecking rules were used to select a given function overload that doesn't involve peppering half the code base with debug prints and then painstakingly poring over a gigantic log (user: achavasse size: 3939)

#include "builtins/builtins.h"

//#define OVL_TC_DEBUG

using namespace goose::sema;

namespace goose::builtins
{
    class OverloadSetInvocationRule : public InvocationRule
    {
        public:
            Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
            {
                auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );

                optional< TypeCheckingContext > bestTCC;
                optional< Term > bestSol;
                const OverloadSet::Overload* bestOvl = nullptr;
                bool ambiguous = false;

#if defined( OVL_TC_DEBUG ) && !defined( NDEBUG )
                cout << "#### Invoking " << pOvlSet->identity() << endl;
#endif

                auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid ) );
                TypeCheckingContext tcc( c );
                for( auto&& [s,ovl,tcc] : pOvlSet->typeCheck( callPat, tcc ) )
                {
                    if( tcc.numUnknownValues() )
                       continue;

                    auto subs = Substitute( s, tcc );

                    // Typechecking rules often end up stripping part of the original type,
                    // and we want to invoke the overload where these removals are minimized.
                    //
                    // Obvious example: if there is an overload that accepts a reference
                    // and one that accepts a value of the same type and we started with a
                    // reference, then we want to call the overload where the typechecking
                    // solution didn't strip the reference.
                    //
                    // So we add the weight of the original arguments to the cost,
                    // and remove the cost of the typechecking solution to account for that.
                    int32_t cost = tcc.cost();
                    cost += GetWeight( callPat );
                    cost -= GetWeight( subs );
                    tcc.setCost( cost );

                    auto score = tcc.score();
                    if( bestTCC && score < bestTCC->score() )
                        continue;

                    auto pps = Postprocess( subs, tcc );
                    if( !pps )
                        continue;

                    if( bestTCC && score == bestTCC->score() )
                    {
                        ambiguous = true;
                        continue;
                    }

                    bestTCC = tcc;
                    bestSol = move( *pps );
                    bestOvl = &ovl;
                    ambiguous = false;
                }

                if( ambiguous )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "ambiguous function call." );
                    return PoisonValue();
                }

                if( !bestSol )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "function arguments mismatch." );
                    return PoisonValue();
                }

#if defined( OVL_TC_DEBUG ) && !defined( NDEBUG )
                bestTCC->DumpParamsTraces( cout );
                cout << endl;
#endif

                return bestOvl->pInvRule->invoke( c, loc, *bestOvl->callee, args, *bestSol, *bestTCC );
            }
    };

    ptr< InvocationRule >& GetOverloadSetInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< OverloadSetInvocationRule >();
        return pRule;
    }

    void SetupOverloadSetInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( Value(
                GetValueType< ptr< OverloadSet > >(),
                ANYTERM( _ ) ) ),
            GetOverloadSetInvocationRule() );
    }
}