Goose  Artifact [a5033072e5]

Artifact a5033072e5374575b6bcd98e87926ddce818bd82347f58def5eb39e5a042579b:

  • File bs/builtins/types/func/build.cpp — part of check-in [b4d5bdf6ec] at 2022-06-18 18:51:47 on branch cir-stack-language —
    • Added a location id to all CIR instructions (needed with the stack based approach to locate intermediate results)
    • Fixed a bunch of verifier errors
    • Re-enabled most verifier tests, other than some requiring to re-implement a few more bits
    (user: zlodo size: 5987) [more...]

#include "builtins/builtins.h"

namespace goose::builtins
{
    Term BuildParamPat( const Context& c, Value type, LocationId loc )
    {
        if( !type.isType() )
            type = ToType( c, type );

        return ValueToEIR( ValuePattern( TSID( param ), ValueToEIR( type ), HOLE( "_"_sid ) ).setLocationId( loc ) );
    }

    optional< FuncType > BuildFuncType( const Context& c, const Value& returnType, const Value& params )
    {
        auto verificationIdentity = VEC( Env::NewUniqueId() );
        c.env()->addVisibilityRule( c.identity(), verificationIdentity );

        auto tv = make_shared< Vector >();
        tv->reserve( TupleSize( params ) );

        bool failed = false;

        uint32_t varId = 0;
        ForEachInTuple( params, [&]( auto&& param )
        {
            if( IsDecl( param ) )
            {
                auto decl = *FromValue< Decl >( param );

                auto type = InvokeOverloadSet( c, c.env()->extConvertFuncParam(),
                    MakeTuple( *EIRToValue( decl.type() ) ) );
                if( type.isPoison() )
                {
                    failed = true;
                    return false;
                }

                tv->append( BuildParamPat( c, type, param.locationId() ) );

                // Bind a stand-in value with the parameters name to be used inside of verification expressions.
                auto paramVerificationIdentity = AppendToVectorTerm( verificationIdentity,
                    TERM( decl.name() ) );

                c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
                    ValueToEIR( BuildComputedValue( decl.type(),
                        cir::VarAddr( varId++, decl.type(), param.locationId() ),
                        cir::Load( decl.type(), param.locationId() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ValueToEIR( param ) );

            return true;
        } );

        if( failed )
            return nullopt;

        // If the return type is non-void, expose @result under the verification identity as a computed value whose type
        // is the function's return type, and the value is a placeholder cir instruction. This will allow verification
        // expressions to refer to the current function's return value as a value of the correct type.
        auto rtTerm = ValueToEIR( returnType );
        if( rtTerm != GetValueType< void >() )
        {
            auto name = "@result"_sid;
            auto retValVerificationIdentity = AppendToVectorTerm( verificationIdentity, TERM( name ) );

            c.env()->storeValue( retValVerificationIdentity, ANYTERM( _ ),
                ValueToEIR( BuildComputedValue( rtTerm, cir::Placeholder( rtTerm, name, returnType.locationId() ) ) ) );
        }

        auto pVerifInfos = make_shared< FuncVerificationInfos >( move( verificationIdentity ) );
        return FuncType( rtTerm, tv, move( pVerifInfos ) );
    }

    Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg )
    {
        if( varArg )
            funcType.setKind( FuncType::Kind::VarArg );
        return Func( funcType, symbol );
    }

    optional< Func > BuildFunc( const Context& c,
        const Term& parentIdentity, const Term& funcIdentity, const Value& returnType,
        const Value& paramsDecl, const ptr< void >& unparsedBody, Context& out_bodyContext )
    {
        auto funcType = BuildFuncType( c, returnType, paramsDecl );
        if( !funcType )
            return nullopt;
        return BuildFunc( c, *funcType, parentIdentity, funcIdentity, paramsDecl, unparsedBody, out_bodyContext );
    }

    Func BuildFunc( const Context& c,
        const FuncType& funcType, const Term& parentIdentity, const Term& funcIdentity,
        const Value& paramsDecl, const ptr< void >& unparsedBody, Context& out_bodyContext )
    {
        // TODO: instead of a normal import rule from the parent scope, we should use
        // a custom visibility rule that deals with variables from the parent context
        // in a special way:
        // If the function body tries to access variables from the parent function,
        // we should invoke an overridable global function that can transform both the
        // variable object and the function type. This way, we can implement/customize
        // smart lexical captures from inside the language itself (and at a minimum, have
        // a default implementation that denies access to outside variables with an error)
        auto funcTypeTerm = ValueToEIR( ToValue( funcType ) );
        auto identity = VEC( funcIdentity, funcTypeTerm );

        assert( parentIdentity != identity );
        c.env()->addVisibilityRule( parentIdentity, identity );

        out_bodyContext = Context( c.env(), identity, funcType.returnType() );
        auto pFuncCIR = c.env()->createCIRFunc( identity );

        return Func( funcType, unparsedBody, get< pvec >( paramsDecl.val() ), pFuncCIR.get() );
    }

    Term BuildCallPatternFromFuncType( const Value& funcType )
    {
        auto ftype = *FromValue< FuncType >( funcType );

        auto apv = make_shared< Vector >();
        apv->reserve( VecSize( ftype.params() ) + 1 );
        apv->append( ftype.returnType() );

        ForEachInVectorTerm( ftype.params(), [&]( auto&& param )
        {
            auto result = Decompose( param,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( result );

            auto&& [sort, type, val, locId] = *result;

            if( sort == TSID( constant ) )
                apv->append( param );
            else
                apv->append( ValueToEIR( ValuePattern( TSID( computed ), type, TERM( ptr< void >() ) ) ) );

            return true;
        } );

        return TERM( apv );
    }
}