Goose  Artifact [04a9b106b3]

Artifact 04a9b106b3d352eb9aaa20a0891de8b56b51149e51405d225acd16641e32785b:

  • File bs/builtins/types/localvar/localvar.cpp — part of check-in [5a361c8b86] at 2019-08-16 20:33:20 on branch trunk — Implemented variable declarations with local type inference. (user: achavasse size: 7434)

#include "builtins/builtins.h"
#include "parse/parse.h"

using namespace empathy::builtins;
using namespace empathy::llr;
using namespace empathy::parse;

namespace empathy::builtins
{
    const Term& LocalVar::PatternTypeT::GetPattern()
    {
        static auto pattern = GetValueType< LocalVar >( MkHole( "T"_sid ) );
        return pattern;
    }

    Value DeclareLocalVar( Parser& p, const Term& type, StringId name, const optional< Value >& initializer )
    {
        auto cfgId = p.cfg()->uniqueId();
        auto index = p.cfg()->getNewTemporaryIndex();

        LocalVar lv( type, cfgId, index );

        auto bb = p.currentBB();
        bb->emplace_back( AllocVar( *ValueFromIRExpr( type ), cfgId, index ) );

        if( initializer )
        {
            p.pushValue( InvokeOverloadSet( p.resolver()->context(),
                p.resolver()->context().env()->extInitializeLocalVar(),
                MakeTuple( ToValue( lv ), *initializer ) ) );
        }
        else
        {
            p.pushValue( InvokeOverloadSet( p.resolver()->context(),
                p.resolver()->context().env()->extInitializeLocalVar(),
                MakeTuple( ToValue( lv ) ) ) );
        }

        auto locVar = ToValue( lv );
        auto identity = AppendToVectorTerm( p.resolver()->context().identity(), name );

        p.resolver()->context().env()->storeValue( identity, ANYTERM( _ ),
            ValueToIRExpr( locVar ) );

        p.resolver()->clearLookAheadCache();
        return locVar;
    }

    Value DeclareLocalVarWithTypeInference( Parser& p, const Term& typeTExpr, StringId name, const Value& initVal )
    {
        const auto& c = p.resolver()->context();

        // To infer the type and obtain a suitable initialization function,
        // we want the declaration to behave as if it were the following function call:
        // Params: ( $T,  _ ( LocalVar[$T], initVal ) initFunc )
        // Args:   ( pat, InitializeLocalVar                   )
        //
        // Where pat is the TNamedDecl's type pattern and initVal is  the initialization
        // expression.

        // We construct the above expressions and unify them. The best solution (if any)
        // will give us the wanted type and the function to invoke to initialize it.

        // Create the _ texpr.
        static auto anyTVar = ValueToIRExpr( ToValue( TVar( "_"_sid ) ) );

        // The $T texpr.
        static auto TTVar = ValueToIRExpr( ToValue( TVar( "T"_sid ) ) );

        // Create the LocalVar[$T] param.
        auto locVarPatParam = ValueToIRExpr( ToValue( TNamedDecl( GetValueType< LocalVar >( TTVar ), "lv"_sid ) ) );

        auto initValPattern = ValueToIRExpr( ValuePattern( TSID( constant ), initVal.type(), MkHole( "_"_sid ) ) );

        // Create the _ ( LocalVar[pat], initType ) initFunc param.
        auto initFuncTFTParam = ValueToIRExpr( ToValue(
            TNamedDecl( ValueToIRExpr( ToValue( TFuncType( DomainAny(), anyTVar, VEC( move( locVarPatParam ), move( initValPattern ) ) ) ) ),
            "initFunc"_sid ) ) );

        // Create our parameter list pattern.
        auto paramPat = VEC(
            *BuildTemplateSignature( c, TTVar ),
            *BuildTemplateSignature( c, initFuncTFTParam )
        );

        // Create our arg list patter,.
        auto args = VEC(
            *BuildTemplateArgPattern( c, typeTExpr ),
            ValueToIRExpr( ToValue( c.env()->extInitializeLocalVar() ) )
        );

        optional< UnificationContext > bestUC;
        optional< Term > bestSol;
        bool ambiguous = false;

        for( auto&& [s, uc] : FullUnify( paramPat, args, c ) )
        {
            if( !bestSol || uc.score() > bestUC->score() )
            {
                bestUC = uc;
                bestSol = s;
                ambiguous = false;
                continue;
            }

            if( uc.score() < bestUC->score() )
                continue;

            if( s != bestSol )
                ambiguous = true;
        }

        if( ambiguous )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "ambiguous variable type inference." );
            return PoisonValue();
        }

        if( !bestSol )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "variable initialization type mismatch." );
            return PoisonValue();
        }

        auto ppCallPat = Postprocess( *bestSol, *bestUC );
        if( !ppCallPat )
            return PoisonValue();

        // Perform the setup of the type template expression. This will create local bindings
        // for tvars ($whatever), if any, making them available for further use.
        TemplateSetup( c, *bestUC, typeTExpr );

        auto callDecomp = Decompose( *ppCallPat,
            Vec(
                SubTerm(),  // locvar
                SubTerm()   // initializer
            )
        );

        auto&& [type, initializer] = *callDecomp;
        auto initializerVal = *ValueFromIRExpr( initializer );

        auto cfgId = p.cfg()->uniqueId();
        auto index = p.cfg()->getNewTemporaryIndex();

        LocalVar lv( type, cfgId, index );

        auto bb = p.currentBB();
        bb->emplace_back( AllocVar( *ValueFromIRExpr( lv.type() ), cfgId, index ) );

        p.pushValue( ResolveInvocation( c, GetInvocationRule( *c.env(), initializerVal ), initializerVal,
            MakeTuple( ToValue( lv ), initVal ) ) );

        auto locVar = ToValue( lv );
        auto identity = AppendToVectorTerm( p.resolver()->context().identity(), name );

        p.resolver()->context().env()->storeValue( identity, ANYTERM( _ ),
            ValueToIRExpr( locVar ) );

        p.resolver()->clearLookAheadCache();
        return locVar;
    }
}

namespace empathy::ir
{
    const Term& Bridge< LocalVarType >::Type()
    {
        return TypeType();
    }

    Value Bridge< LocalVarType >::ToValue( const LocalVarType& t )
    {
        return Value( Type(), TVEC( TSID( local_var ), t.type() ) );
    }

    Value Bridge< LocalVarType >::ToValue( const Term& type )
    {
        return Value( Type(), TVEC( TSID( local_var ), type ) );
    }

    optional< LocalVarType > Bridge< LocalVarType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "local_var"_sid ),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [type] = *result;
        return LocalVarType( move( type ) );
    }

    Term Bridge< LocalVar >::Type( const Term& type )
    {
        return ValueToIRExpr( ToValue< LocalVarType >( type ) );
    }

    Value Bridge< LocalVar >::ToValue( const LocalVar& lv )
    {
        return Value( Type( lv.type() ), VEC( TERM( lv.cfgId() ), TERM( lv.index() ) ) );
    }

    optional< LocalVar > Bridge< LocalVar >::FromValue( const Value& v )
    {
        auto t = FromValue< LocalVarType >( *ValueFromIRExpr( v.type() ) );
        if( !t )
            return nullopt;

        auto result = Decompose( v.val(),
            Vec(
                Val< uint32_t >(),
                Val< uint32_t >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [cfgId,index] = *result;

        return LocalVar( move( t->type() ), cfgId, index );
    }
}